FINERACT-2374: Advance Accounting rule for classification type
This commit is contained in:
committed by
Adam Saghy
parent
6d3e5fa150
commit
cb70555485
+12
-2
@@ -68,10 +68,20 @@ public class ProductToGLAccountMapping extends AbstractPersistableCustom<Long> {
|
|||||||
@JoinColumn(name = "charge_off_reason_id", nullable = true)
|
@JoinColumn(name = "charge_off_reason_id", nullable = true)
|
||||||
private CodeValue chargeOffReason;
|
private CodeValue chargeOffReason;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "capitalized_income_classification_id", nullable = true)
|
||||||
|
private CodeValue capitalizedIncomeClassification;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "buydown_fee_classification_id", nullable = true)
|
||||||
|
private CodeValue buydownFeeClassification;
|
||||||
|
|
||||||
public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType,
|
public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType,
|
||||||
final int financialAccountType, final CodeValue chargeOffReason) {
|
final int financialAccountType, final CodeValue chargeOffReason, final CodeValue capitalizedIncomeClassification,
|
||||||
|
final CodeValue buydownFeeClassification) {
|
||||||
|
|
||||||
return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType)
|
return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType)
|
||||||
.setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReason);
|
.setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReason)
|
||||||
|
.setCapitalizedIncomeClassification(capitalizedIncomeClassification).setBuydownFeeClassification(buydownFeeClassification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-2
@@ -35,7 +35,7 @@ public interface ProductToGLAccountMappingRepository
|
|||||||
@Param("productType") int productType, @Param("financialAccountType") int financialAccountType,
|
@Param("productType") int productType, @Param("financialAccountType") int financialAccountType,
|
||||||
@Param("chargeId") Long ChargeId);
|
@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 and mapping.chargeOffReason is NULL")
|
@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 and mapping.chargeOffReason is NULL and mapping.capitalizedIncomeClassification is NULL and mapping.buydownFeeClassification is NULL")
|
||||||
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
|
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
|
||||||
@Param("financialAccountType") int financialAccountType);
|
@Param("financialAccountType") int financialAccountType);
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ public interface ProductToGLAccountMappingRepository
|
|||||||
ProductToGLAccountMapping findChargeOffReasonMapping(@Param("productId") Long productId, @Param("productType") Integer productType,
|
ProductToGLAccountMapping findChargeOffReasonMapping(@Param("productId") Long productId, @Param("productType") Integer productType,
|
||||||
@Param("chargeOffReasonId") Long chargeOffReasonId);
|
@Param("chargeOffReasonId") Long chargeOffReasonId);
|
||||||
|
|
||||||
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge IS NULL AND mapping.paymentType IS NULL AND mapping.chargeOffReason IS NULL")
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge IS NULL AND mapping.paymentType IS NULL AND mapping.chargeOffReason IS NULL AND mapping.capitalizedIncomeClassification is NULL AND mapping.buydownFeeClassification is NULL")
|
||||||
List<ProductToGLAccountMapping> findAllRegularMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
|
List<ProductToGLAccountMapping> findAllRegularMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
|
||||||
|
|
||||||
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.paymentType is not NULL")
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.paymentType is not NULL")
|
||||||
@@ -82,4 +82,21 @@ public interface ProductToGLAccountMappingRepository
|
|||||||
|
|
||||||
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge.penalty = FALSE")
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId AND mapping.productType =:productType AND mapping.charge.penalty = FALSE")
|
||||||
List<ProductToGLAccountMapping> findAllFeeMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
|
List<ProductToGLAccountMapping> findAllFeeMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
|
||||||
|
|
||||||
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.capitalizedIncomeClassification is not NULL")
|
||||||
|
List<ProductToGLAccountMapping> findAllCapitalizedIncomeClassificationsMappings(@Param("productId") Long productId,
|
||||||
|
@Param("productType") int productType);
|
||||||
|
|
||||||
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.buydownFeeClassification is not NULL")
|
||||||
|
List<ProductToGLAccountMapping> findAllBuyDownFeeClassificationsMappings(@Param("productId") Long productId,
|
||||||
|
@Param("productType") int productType);
|
||||||
|
|
||||||
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.buydownFeeClassification.id = :classificationId AND mapping.productId = :productId AND mapping.productType = :productType")
|
||||||
|
ProductToGLAccountMapping findBuydownFeeClassificationMapping(@Param("productId") Long productId,
|
||||||
|
@Param("productType") Integer productType, @Param("classificationId") Long classificationId);
|
||||||
|
|
||||||
|
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.capitalizedIncomeClassification.id = :classificationId AND mapping.productId = :productId AND mapping.productType = :productType")
|
||||||
|
ProductToGLAccountMapping findCapitalizedIncomeClassificationMapping(@Param("productId") Long productId,
|
||||||
|
@Param("productType") Integer productType, @Param("classificationId") Long classificationId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+160
@@ -227,6 +227,30 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveClassificationToGLAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
final Map<String, Object> changes, final PortfolioProductType portfolioProductType,
|
||||||
|
final LoanProductAccountingParams classificationParameter) {
|
||||||
|
|
||||||
|
final String arrayName = classificationParameter.getValue();
|
||||||
|
final JsonArray classificationToIncomeAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(arrayName, element);
|
||||||
|
|
||||||
|
if (classificationToIncomeAccountMappingArray != null) {
|
||||||
|
if (changes != null) {
|
||||||
|
changes.put(arrayName, command.jsonFragment(arrayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < classificationToIncomeAccountMappingArray.size(); i++) {
|
||||||
|
final JsonObject jsonObject = classificationToIncomeAccountMappingArray.get(i).getAsJsonObject();
|
||||||
|
final Long classificationId = jsonObject.get(LoanProductAccountingParams.CLASSIFICATION_CODE_VALUE_ID.getValue())
|
||||||
|
.getAsLong();
|
||||||
|
final Long incomeAccountId = jsonObject.get(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()).getAsLong();
|
||||||
|
|
||||||
|
saveClassificationToIncomeMapping(productId, classificationId, incomeAccountId, portfolioProductType,
|
||||||
|
classificationParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param command
|
* @param command
|
||||||
* @param element
|
* @param element
|
||||||
@@ -448,6 +472,75 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateClassificationToGLAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
|
final Map<String, Object> changes, final PortfolioProductType portfolioProductType,
|
||||||
|
final LoanProductAccountingParams classificationParameter) {
|
||||||
|
|
||||||
|
final List<ProductToGLAccountMapping> existingClassificationToGLAccountMappings = classificationParameter
|
||||||
|
.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)
|
||||||
|
? this.accountMappingRepository.findAllCapitalizedIncomeClassificationsMappings(productId,
|
||||||
|
portfolioProductType.getValue())
|
||||||
|
: this.accountMappingRepository.findAllBuyDownFeeClassificationsMappings(productId,
|
||||||
|
portfolioProductType.getValue());
|
||||||
|
|
||||||
|
final JsonArray classificationToGLAccountMappingArray = this.fromApiJsonHelper
|
||||||
|
.extractJsonArrayNamed(classificationParameter.getValue(), element);
|
||||||
|
|
||||||
|
final Map<Long, Long> inputClassificationToGLAccountMap = new HashMap<>();
|
||||||
|
|
||||||
|
final Set<Long> existingClassifications = new HashSet<>();
|
||||||
|
if (classificationToGLAccountMappingArray != null) {
|
||||||
|
if (changes != null) {
|
||||||
|
changes.put(classificationParameter.getValue(), command.jsonFragment(classificationParameter.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < classificationToGLAccountMappingArray.size(); i++) {
|
||||||
|
final JsonObject jsonObject = classificationToGLAccountMappingArray.get(i).getAsJsonObject();
|
||||||
|
final Long incomeGlAccountId = jsonObject.get(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()).getAsLong();
|
||||||
|
final Long classificationCodeValueId = jsonObject.get(LoanProductAccountingParams.CLASSIFICATION_CODE_VALUE_ID.getValue())
|
||||||
|
.getAsLong();
|
||||||
|
inputClassificationToGLAccountMap.put(classificationCodeValueId, incomeGlAccountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If input map is empty, delete all existing mappings
|
||||||
|
if (inputClassificationToGLAccountMap.isEmpty()) {
|
||||||
|
this.accountMappingRepository.deleteAllInBatch(existingClassificationToGLAccountMappings);
|
||||||
|
} else {
|
||||||
|
for (final ProductToGLAccountMapping existingClassificationToGLAccountMapping : existingClassificationToGLAccountMappings) {
|
||||||
|
final Long currentClassificationId = classificationParameter
|
||||||
|
.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)
|
||||||
|
? existingClassificationToGLAccountMapping.getCapitalizedIncomeClassification().getId()
|
||||||
|
: existingClassificationToGLAccountMapping.getBuydownFeeClassification().getId();
|
||||||
|
|
||||||
|
if (currentClassificationId != null) {
|
||||||
|
existingClassifications.add(currentClassificationId);
|
||||||
|
// update existing mappings (if required)
|
||||||
|
if (inputClassificationToGLAccountMap.containsKey(currentClassificationId)) {
|
||||||
|
final Long newGLAccountId = inputClassificationToGLAccountMap.get(currentClassificationId);
|
||||||
|
if (!newGLAccountId.equals(existingClassificationToGLAccountMapping.getGlAccount().getId())) {
|
||||||
|
final Optional<GLAccount> glAccount = accountRepository.findById(newGLAccountId);
|
||||||
|
if (glAccount.isPresent()) {
|
||||||
|
existingClassificationToGLAccountMapping.setGlAccount(glAccount.get());
|
||||||
|
this.accountMappingRepository.saveAndFlush(existingClassificationToGLAccountMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // deleted previous record
|
||||||
|
else {
|
||||||
|
this.accountMappingRepository.delete(existingClassificationToGLAccountMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only the newly added
|
||||||
|
for (Map.Entry<Long, Long> entry : inputClassificationToGLAccountMap.entrySet().stream()
|
||||||
|
.filter(e -> !existingClassifications.contains(e.getKey())).toList()) {
|
||||||
|
saveClassificationToIncomeMapping(productId, entry.getKey(), entry.getValue(), portfolioProductType,
|
||||||
|
classificationParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param productId
|
* @param productId
|
||||||
*
|
*
|
||||||
@@ -514,6 +607,39 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveClassificationToIncomeMapping(final Long productId, final Long classificationId, final Long incomeAccountId,
|
||||||
|
final PortfolioProductType portfolioProductType, final LoanProductAccountingParams classificationParameter) {
|
||||||
|
|
||||||
|
final Optional<GLAccount> glAccount = accountRepository.findById(incomeAccountId);
|
||||||
|
|
||||||
|
boolean classificationMappingExists = false;
|
||||||
|
if (classificationParameter.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)) {
|
||||||
|
classificationMappingExists = this.accountMappingRepository
|
||||||
|
.findAllCapitalizedIncomeClassificationsMappings(productId, portfolioProductType.getValue()).stream()
|
||||||
|
.anyMatch(mapping -> mapping.getCapitalizedIncomeClassification().getId().equals(classificationId));
|
||||||
|
} else {
|
||||||
|
classificationMappingExists = this.accountMappingRepository
|
||||||
|
.findAllBuyDownFeeClassificationsMappings(productId, portfolioProductType.getValue()).stream()
|
||||||
|
.anyMatch(mapping -> mapping.getBuydownFeeClassification().getId().equals(classificationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<CodeValue> codeValueOptional = codeValueRepository.findById(classificationId);
|
||||||
|
|
||||||
|
if (glAccount.isPresent() && !classificationMappingExists && codeValueOptional.isPresent()) {
|
||||||
|
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount.get())
|
||||||
|
.setProductId(productId).setProductType(portfolioProductType.getValue())
|
||||||
|
.setFinancialAccountType(CashAccountsForLoan.CLASSIFICATION_INCOME.getValue());
|
||||||
|
|
||||||
|
if (classificationParameter.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)) {
|
||||||
|
accountMapping.setCapitalizedIncomeClassification(codeValueOptional.get());
|
||||||
|
} else {
|
||||||
|
accountMapping.setBuydownFeeClassification(codeValueOptional.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.accountMappingRepository.saveAndFlush(accountMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<GLAccountType> getAllowedAccountTypesForFeeMapping() {
|
private List<GLAccountType> getAllowedAccountTypesForFeeMapping() {
|
||||||
List<GLAccountType> allowedAccountTypes = new ArrayList<>();
|
List<GLAccountType> allowedAccountTypes = new ArrayList<>();
|
||||||
allowedAccountTypes.add(GLAccountType.INCOME);
|
allowedAccountTypes.add(GLAccountType.INCOME);
|
||||||
@@ -610,4 +736,38 @@ public class ProductToGLAccountMappingHelper {
|
|||||||
throw new PlatformApiDataValidationException(validationErrors);
|
throw new PlatformApiDataValidationException(validationErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateClassificationMappingsInDatabase(final List<JsonObject> mappings, final String dataCodeName) {
|
||||||
|
final List<ApiParameterError> validationErrors = new ArrayList<>();
|
||||||
|
|
||||||
|
for (JsonObject jsonObject : mappings) {
|
||||||
|
final Long incomeGlAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(),
|
||||||
|
jsonObject);
|
||||||
|
final Long classificationCodeValueId = this.fromApiJsonHelper
|
||||||
|
.extractLongNamed(LoanProductAccountingParams.CLASSIFICATION_CODE_VALUE_ID.getValue(), jsonObject);
|
||||||
|
|
||||||
|
// Validation: classificationCodeValueId must exist in the database
|
||||||
|
final CodeValue codeValue = this.codeValueRepository.findByCodeNameAndId(dataCodeName, classificationCodeValueId);
|
||||||
|
if (codeValue == null) {
|
||||||
|
validationErrors.add(ApiParameterError.parameterError("validation.msg.classification.invalid",
|
||||||
|
"Classification with ID " + classificationCodeValueId + " does not exist", dataCodeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation: expenseGLAccountId must exist as a valid Expense GL account
|
||||||
|
final Optional<GLAccount> glAccount = accountRepository.findById(incomeGlAccountId);
|
||||||
|
|
||||||
|
if (glAccount.isEmpty() || !GLAccountType.fromInt(glAccount.get().getType()).isIncomeType()) {
|
||||||
|
validationErrors.add(ApiParameterError.parameterError("validation.msg.glaccount.not.found",
|
||||||
|
"GL Account with ID " + incomeGlAccountId + " does not exist or is not an Income GL account",
|
||||||
|
LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw all collected validation errors, if any
|
||||||
|
if (!validationErrors.isEmpty()) {
|
||||||
|
throw new PlatformApiDataValidationException(validationErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -20,8 +20,10 @@ package org.apache.fineract.accounting.producttoaccountmapping.service;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ClassificationToGLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
|
|
||||||
public interface ProductToGLAccountMappingReadPlatformService {
|
public interface ProductToGLAccountMappingReadPlatformService {
|
||||||
@@ -49,4 +51,7 @@ public interface ProductToGLAccountMappingReadPlatformService {
|
|||||||
List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId);
|
List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId);
|
||||||
|
|
||||||
List<ChargeOffReasonToGLAccountMapper> fetchChargeOffReasonMappingsForLoanProduct(Long loanProductId);
|
List<ChargeOffReasonToGLAccountMapper> fetchChargeOffReasonMappingsForLoanProduct(Long loanProductId);
|
||||||
|
|
||||||
|
List<ClassificationToGLAccountData> fetchClassificationMappingsForLoanProduct(Long loanProductId,
|
||||||
|
LoanProductAccountingParams classificationParameter);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-11
@@ -30,6 +30,7 @@ import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsFor
|
|||||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingDataParams;
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingDataParams;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingDataParams;
|
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingDataParams;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
|
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.common.AccountingRuleType;
|
import org.apache.fineract.accounting.common.AccountingRuleType;
|
||||||
@@ -37,14 +38,15 @@ import org.apache.fineract.accounting.common.AccountingValidations;
|
|||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ClassificationToGLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
|
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
||||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
|
import org.apache.fineract.infrastructure.codes.mapper.CodeValueMapper;
|
||||||
import org.apache.fineract.portfolio.PortfolioProductType;
|
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -52,9 +54,8 @@ import org.springframework.stereotype.Service;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ProductToGLAccountMappingReadPlatformServiceImpl implements ProductToGLAccountMappingReadPlatformService {
|
public class ProductToGLAccountMappingReadPlatformServiceImpl implements ProductToGLAccountMappingReadPlatformService {
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
|
||||||
|
|
||||||
private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository;
|
private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository;
|
||||||
|
private final CodeValueMapper codeValueMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) {
|
public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) {
|
||||||
@@ -283,14 +284,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
final String glAccountName = mapping.getGlAccount().getName();
|
final String glAccountName = mapping.getGlAccount().getName();
|
||||||
final String glCode = mapping.getGlAccount().getGlCode();
|
final String glCode = mapping.getGlAccount().getGlCode();
|
||||||
final GLAccountData chargeOffExpenseAccount = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
|
final GLAccountData chargeOffExpenseAccount = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
|
||||||
final Long chargeOffReasonId = mapping.getChargeOffReason().getId();
|
final CodeValueData chargeOffReasonsCodeValue = codeValueMapper.map(mapping.getChargeOffReason());
|
||||||
final String codeValue = mapping.getChargeOffReason().getLabel();
|
|
||||||
final String codeDescription = mapping.getChargeOffReason().getDescription();
|
|
||||||
final Integer orderPosition = mapping.getChargeOffReason().getPosition();
|
|
||||||
final boolean isActive = mapping.getChargeOffReason().isActive();
|
|
||||||
final boolean isMandatory = mapping.getChargeOffReason().isMandatory();
|
|
||||||
final CodeValueData chargeOffReasonsCodeValue = CodeValueData.builder().id(chargeOffReasonId).name(codeValue)
|
|
||||||
.description(codeDescription).position(orderPosition).active(isActive).mandatory(isMandatory).build();
|
|
||||||
|
|
||||||
final ChargeOffReasonToGLAccountMapper chargeOffReasonToGLAccountMapper = new ChargeOffReasonToGLAccountMapper()
|
final ChargeOffReasonToGLAccountMapper chargeOffReasonToGLAccountMapper = new ChargeOffReasonToGLAccountMapper()
|
||||||
.setChargeOffReasonCodeValue(chargeOffReasonsCodeValue).setExpenseAccount(chargeOffExpenseAccount);
|
.setChargeOffReasonCodeValue(chargeOffReasonsCodeValue).setExpenseAccount(chargeOffExpenseAccount);
|
||||||
@@ -299,6 +293,35 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
return chargeOffReasonToGLAccountMappers;
|
return chargeOffReasonToGLAccountMappers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ClassificationToGLAccountData> fetchClassificationMappings(final PortfolioProductType portfolioProductType,
|
||||||
|
final Long loanProductId, LoanProductAccountingParams classificationParameter) {
|
||||||
|
final List<ProductToGLAccountMapping> mappings = classificationParameter
|
||||||
|
.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)
|
||||||
|
? productToGLAccountMappingRepository.findAllCapitalizedIncomeClassificationsMappings(loanProductId,
|
||||||
|
portfolioProductType.getValue())
|
||||||
|
: productToGLAccountMappingRepository.findAllBuyDownFeeClassificationsMappings(loanProductId,
|
||||||
|
portfolioProductType.getValue());
|
||||||
|
|
||||||
|
productToGLAccountMappingRepository.findAllChargeOffReasonsMappings(loanProductId, portfolioProductType.getValue());
|
||||||
|
List<ClassificationToGLAccountData> classificationToGLAccountMappers = mappings.isEmpty() ? null : new ArrayList<>();
|
||||||
|
for (final ProductToGLAccountMapping mapping : mappings) {
|
||||||
|
final Long glAccountId = mapping.getGlAccount().getId();
|
||||||
|
final String glAccountName = mapping.getGlAccount().getName();
|
||||||
|
final String glCode = mapping.getGlAccount().getGlCode();
|
||||||
|
final GLAccountData glAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
|
||||||
|
|
||||||
|
final CodeValueData classificationCodeValue = classificationParameter
|
||||||
|
.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)
|
||||||
|
? codeValueMapper.map(mapping.getCapitalizedIncomeClassification())
|
||||||
|
: codeValueMapper.map(mapping.getBuydownFeeClassification());
|
||||||
|
|
||||||
|
final ClassificationToGLAccountData classificationToGLAccountMapper = new ClassificationToGLAccountData()
|
||||||
|
.setClassificationCodeValue(classificationCodeValue).setIncomeAccount(glAccountData);
|
||||||
|
classificationToGLAccountMappers.add(classificationToGLAccountMapper);
|
||||||
|
}
|
||||||
|
return classificationToGLAccountMappers;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) {
|
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) {
|
||||||
|
|
||||||
@@ -344,6 +367,12 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
|||||||
return fetchChargeOffReasonMappings(PortfolioProductType.LOAN, loanProductId);
|
return fetchChargeOffReasonMappings(PortfolioProductType.LOAN, loanProductId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ClassificationToGLAccountData> fetchClassificationMappingsForLoanProduct(Long loanProductId,
|
||||||
|
LoanProductAccountingParams classificationParameter) {
|
||||||
|
return fetchClassificationMappings(PortfolioProductType.LOAN, loanProductId, classificationParameter);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> setAccrualPeriodicSavingsProductToGLAccountMaps(final List<ProductToGLAccountMapping> mappings) {
|
private Map<String, Object> setAccrualPeriodicSavingsProductToGLAccountMaps(final List<ProductToGLAccountMapping> mappings) {
|
||||||
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
|
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -53,7 +53,9 @@ public final class AccountingConstants {
|
|||||||
INCOME_FROM_CHARGE_OFF_PENALTY(18), //
|
INCOME_FROM_CHARGE_OFF_PENALTY(18), //
|
||||||
INCOME_FROM_GOODWILL_CREDIT_INTEREST(19), //
|
INCOME_FROM_GOODWILL_CREDIT_INTEREST(19), //
|
||||||
INCOME_FROM_GOODWILL_CREDIT_FEES(20), //
|
INCOME_FROM_GOODWILL_CREDIT_FEES(20), //
|
||||||
INCOME_FROM_GOODWILL_CREDIT_PENALTY(21); //
|
INCOME_FROM_GOODWILL_CREDIT_PENALTY(21), //
|
||||||
|
CLASSIFICATION_INCOME(22), //
|
||||||
|
;
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|
||||||
@@ -185,6 +187,9 @@ public final class AccountingConstants {
|
|||||||
INCOME_FROM_CAPITALIZATION("incomeFromCapitalizationAccountId"), //
|
INCOME_FROM_CAPITALIZATION("incomeFromCapitalizationAccountId"), //
|
||||||
BUY_DOWN_EXPENSE("buyDownExpenseAccountId"), //
|
BUY_DOWN_EXPENSE("buyDownExpenseAccountId"), //
|
||||||
INCOME_FROM_BUY_DOWN("incomeFromBuyDownAccountId"), //
|
INCOME_FROM_BUY_DOWN("incomeFromBuyDownAccountId"), //
|
||||||
|
CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS("capitalizedIncomeClassificationToIncomeAccountMappings"), //
|
||||||
|
BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS("buydownfeeClassificationToIncomeAccountMappings"), //
|
||||||
|
CLASSIFICATION_CODE_VALUE_ID("classificationCodeValueId"), //
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.journalentry.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class AdvancedMappingtDTO {
|
||||||
|
|
||||||
|
private final Long referenceValueId;
|
||||||
|
private final BigDecimal amount;
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ClassificationToGLAccountData implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private CodeValueData classificationCodeValue;
|
||||||
|
private GLAccountData incomeAccount;
|
||||||
|
}
|
||||||
+5
-1
@@ -23,7 +23,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
/**
|
/**
|
||||||
* Created by sanyam on 30/7/17.
|
* Created by sanyam on 30/7/17.
|
||||||
*/
|
*/
|
||||||
final class CodeValuesApiResourceSwagger {
|
public final class CodeValuesApiResourceSwagger {
|
||||||
|
|
||||||
private CodeValuesApiResourceSwagger() {
|
private CodeValuesApiResourceSwagger() {
|
||||||
|
|
||||||
@@ -44,6 +44,10 @@ final class CodeValuesApiResourceSwagger {
|
|||||||
public String description;
|
public String description;
|
||||||
@Schema(example = "0")
|
@Schema(example = "0")
|
||||||
public Integer position;
|
public Integer position;
|
||||||
|
@Schema(example = "true")
|
||||||
|
public Boolean active;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public Boolean mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "PostCodeValuesDataRequest")
|
@Schema(description = "PostCodeValuesDataRequest")
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.codes.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
|
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
@Mapper(config = MapstructMapperConfig.class)
|
||||||
|
public interface CodeValueMapper {
|
||||||
|
|
||||||
|
@Mapping(target = "name", source = "label")
|
||||||
|
CodeValueData map(CodeValue source);
|
||||||
|
|
||||||
|
List<CodeValueData> map(List<CodeValue> source);
|
||||||
|
|
||||||
|
}
|
||||||
+24
@@ -149,6 +149,30 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
|
|||||||
updateChargeOffReasonToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
updateChargeOffReasonToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveCapitalizedIncomeClassificationToIncomeAccountMappings(final JsonCommand command, final JsonElement element,
|
||||||
|
final Long productId, final Map<String, Object> changes) {
|
||||||
|
saveClassificationToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateCapitalizedIncomeClassificationToIncomeAccountMappings(final JsonCommand command, final JsonElement element,
|
||||||
|
final Long productId, final Map<String, Object> changes) {
|
||||||
|
updateClassificationToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveBuyDownFeeClassificationToIncomeAccountMappings(final JsonCommand command, final JsonElement element,
|
||||||
|
final Long productId, final Map<String, Object> changes) {
|
||||||
|
saveClassificationToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN,
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBuyDownFeeClassificationToIncomeAccountMappings(final JsonCommand command, final JsonElement element,
|
||||||
|
final Long productId, final Map<String, Object> changes) {
|
||||||
|
updateClassificationToGLAccountMappings(command, element, productId, changes, PortfolioProductType.LOAN,
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
|
||||||
final Map<String, Object> changes) {
|
final Map<String, Object> changes) {
|
||||||
// update both fee and penalty charges
|
// update both fee and penalty charges
|
||||||
|
|||||||
+3
@@ -25,6 +25,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.AdvancedMappingtDTO;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@@ -47,5 +48,7 @@ public class AccountingBridgeDataDTO {
|
|||||||
private boolean isWrittenOff;
|
private boolean isWrittenOff;
|
||||||
private List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
private List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
||||||
private boolean merchantBuyDownFee;
|
private boolean merchantBuyDownFee;
|
||||||
|
private List<AdvancedMappingtDTO> buydownFeeClassificationCodeValue;
|
||||||
|
private List<AdvancedMappingtDTO> capitalizedIncomeClassificationCodeValue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -61,4 +61,13 @@ public interface LoanAmortizationAllocationMappingRepository
|
|||||||
WHERE laam.baseLoanTransactionId = :baseLoanTransactionId AND laam.loanId = :loanId
|
WHERE laam.baseLoanTransactionId = :baseLoanTransactionId AND laam.loanId = :loanId
|
||||||
""")
|
""")
|
||||||
BigDecimal calculateAlreadyAmortizedAmount(@Param("baseLoanTransactionId") Long baseLoanTransactionId, @Param("loanId") Long loanId);
|
BigDecimal calculateAlreadyAmortizedAmount(@Param("baseLoanTransactionId") Long baseLoanTransactionId, @Param("loanId") Long loanId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT laam FROM LoanAmortizationAllocationMapping laam
|
||||||
|
JOIN LoanTransaction at ON at.id = laam.baseLoanTransactionId
|
||||||
|
WHERE laam.amortizationLoanTransactionId = :amortizationLoanTransactionId
|
||||||
|
AND laam.loanId = :loanId
|
||||||
|
""")
|
||||||
|
List<LoanAmortizationAllocationMapping> fetchLoanTransactionAllocationByAmortizationLoanTransactionId(
|
||||||
|
@Param("amortizationLoanTransactionId") Long amortizationLoanTransactionId, @Param("loanId") Long loanId);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -24,6 +24,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting;
|
import org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||||
@@ -473,4 +474,11 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
|||||||
boolean existsNonReversedByLoanAndTypeAndDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type,
|
boolean existsNonReversedByLoanAndTypeAndDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type,
|
||||||
@Param("transactionDate") LocalDate transactionDate);
|
@Param("transactionDate") LocalDate transactionDate);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT lt.classification
|
||||||
|
FROM LoanTransaction lt
|
||||||
|
WHERE lt.id = :transactionId
|
||||||
|
""")
|
||||||
|
CodeValue fetchClassificationCodeValueByTransactionId(@Param("transactionId") Long transactionId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-12
@@ -24,6 +24,7 @@ import java.time.LocalDate;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.apache.fineract.infrastructure.codes.api.CodeValuesApiResourceSwagger.GetCodeValuesDataResponse;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||||
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||||
@@ -300,6 +301,8 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
||||||
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
||||||
public List<PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
|
public List<PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
|
||||||
|
public List<PostLoanProductsRequest.PostClassificationToIncomeAccountMappings> buydownfeeClassificationToIncomeAccountMappings;
|
||||||
|
public List<PostLoanProductsRequest.PostClassificationToIncomeAccountMappings> capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
public List<LoanProductChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
public List<LoanProductChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
||||||
|
|
||||||
// Multi Disburse
|
// Multi Disburse
|
||||||
@@ -378,6 +381,16 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
public Long expenseAccountId;
|
public Long expenseAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class PostClassificationToIncomeAccountMappings {
|
||||||
|
|
||||||
|
private PostClassificationToIncomeAccountMappings() {}
|
||||||
|
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long classificationCodeValueId;
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long incomeAccountId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "PostLoanProductsResponse")
|
@Schema(description = "PostLoanProductsResponse")
|
||||||
@@ -1168,6 +1181,8 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
public List<StringEnumOptionData> buyDownFeeCalculationTypeOptions;
|
public List<StringEnumOptionData> buyDownFeeCalculationTypeOptions;
|
||||||
public List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
public List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
||||||
public List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
public List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
||||||
|
public List<GetCodeValuesDataResponse> capitalizedIncomeClassificationOptions;
|
||||||
|
public List<GetCodeValuesDataResponse> buydownFeeClassificationOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "GetLoanProductsProductIdResponse")
|
@Schema(description = "GetLoanProductsProductIdResponse")
|
||||||
@@ -1294,6 +1309,26 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
public Long fundSourceAccountId;
|
public Long fundSourceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class GetGLAccountData {
|
||||||
|
|
||||||
|
private GetGLAccountData() {}
|
||||||
|
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Long id;
|
||||||
|
@Schema(example = "Written off")
|
||||||
|
public String name;
|
||||||
|
@Schema(example = "e4")
|
||||||
|
public String glCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class GetClassificationToIncomeAccountMappings {
|
||||||
|
|
||||||
|
private GetClassificationToIncomeAccountMappings() {}
|
||||||
|
|
||||||
|
public GetCodeValuesDataResponse classificationCodeValue;
|
||||||
|
public GetGLAccountData incomeAccount;
|
||||||
|
}
|
||||||
|
|
||||||
static final class GetChargeOffReasonToExpenseAccountMappings {
|
static final class GetChargeOffReasonToExpenseAccountMappings {
|
||||||
|
|
||||||
private GetChargeOffReasonToExpenseAccountMappings() {}
|
private GetChargeOffReasonToExpenseAccountMappings() {}
|
||||||
@@ -1317,18 +1352,6 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean mandatory;
|
public Boolean mandatory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetGLAccountData {
|
|
||||||
|
|
||||||
private GetGLAccountData() {}
|
|
||||||
|
|
||||||
@Schema(example = "1")
|
|
||||||
public Long id;
|
|
||||||
@Schema(example = "Written off")
|
|
||||||
public String name;
|
|
||||||
@Schema(example = "e4")
|
|
||||||
public String glCode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class GetLoanFeeToIncomeAccountMappings {
|
static final class GetLoanFeeToIncomeAccountMappings {
|
||||||
@@ -1516,6 +1539,11 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
public List<StringEnumOptionData> buyDownFeeCalculationTypeOptions;
|
public List<StringEnumOptionData> buyDownFeeCalculationTypeOptions;
|
||||||
public List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
public List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
||||||
public List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
public List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
||||||
|
public List<GetCodeValuesDataResponse> capitalizedIncomeClassificationOptions;
|
||||||
|
public List<GetCodeValuesDataResponse> buydownFeeClassificationOptions;
|
||||||
|
public List<GetClassificationToIncomeAccountMappings> buydownFeeClassificationToIncomeAccountMappings;
|
||||||
|
public List<GetClassificationToIncomeAccountMappings> capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "PutLoanProductsProductIdRequest")
|
@Schema(description = "PutLoanProductsProductIdRequest")
|
||||||
@@ -1746,6 +1774,8 @@ public final class LoanProductsApiResourceSwagger {
|
|||||||
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
||||||
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
||||||
public List<PostLoanProductsRequest.PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
|
public List<PostLoanProductsRequest.PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
|
||||||
|
public List<PostLoanProductsRequest.PostClassificationToIncomeAccountMappings> buydownfeeClassificationToIncomeAccountMappings;
|
||||||
|
public List<PostLoanProductsRequest.PostClassificationToIncomeAccountMappings> capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
public List<LoanProductChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
public List<LoanProductChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
public Boolean enableAccrualActivityPosting;
|
public Boolean enableAccrualActivityPosting;
|
||||||
|
|||||||
+21
-2
@@ -32,6 +32,7 @@ import org.apache.fineract.accounting.common.AccountingRuleType;
|
|||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ClassificationToGLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
import org.apache.fineract.infrastructure.core.api.ApiFacingEnum;
|
import org.apache.fineract.infrastructure.core.api.ApiFacingEnum;
|
||||||
@@ -261,6 +262,11 @@ public class LoanProductData implements Serializable {
|
|||||||
private List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
private List<StringEnumOptionData> buyDownFeeStrategyOptions;
|
||||||
private List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
private List<StringEnumOptionData> buyDownFeeIncomeTypeOptions;
|
||||||
|
|
||||||
|
private final List<CodeValueData> capitalizedIncomeClassificationOptions;
|
||||||
|
private final List<CodeValueData> buydownFeeClassificationOptions;
|
||||||
|
private List<ClassificationToGLAccountData> capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
|
private List<ClassificationToGLAccountData> buydownFeeClassificationToIncomeAccountMappings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used when returning lookup information about loan product for dropdowns.
|
* Used when returning lookup information about loan product for dropdowns.
|
||||||
*/
|
*/
|
||||||
@@ -822,12 +828,16 @@ public class LoanProductData implements Serializable {
|
|||||||
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
|
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings,
|
||||||
final Collection<ChargeToGLAccountMapper> feeToGLAccountMappings,
|
final Collection<ChargeToGLAccountMapper> feeToGLAccountMappings,
|
||||||
final Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings,
|
final Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings,
|
||||||
final List<ChargeOffReasonToGLAccountMapper> chargeOffReasonToGLAccountMappings) {
|
final List<ChargeOffReasonToGLAccountMapper> chargeOffReasonToGLAccountMappings,
|
||||||
|
final List<ClassificationToGLAccountData> capitalizedIncomeClassificationToIncomeAccountMappings,
|
||||||
|
final List<ClassificationToGLAccountData> buydownFeeClassificationToIncomeAccountMappings) {
|
||||||
productData.accountingMappings = accountingMappings;
|
productData.accountingMappings = accountingMappings;
|
||||||
productData.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
|
productData.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
|
||||||
productData.feeToIncomeAccountMappings = feeToGLAccountMappings;
|
productData.feeToIncomeAccountMappings = feeToGLAccountMappings;
|
||||||
productData.penaltyToIncomeAccountMappings = penaltyToGLAccountMappings;
|
productData.penaltyToIncomeAccountMappings = penaltyToGLAccountMappings;
|
||||||
productData.chargeOffReasonToExpenseAccountMappings = chargeOffReasonToGLAccountMappings;
|
productData.chargeOffReasonToExpenseAccountMappings = chargeOffReasonToGLAccountMappings;
|
||||||
|
productData.capitalizedIncomeClassificationToIncomeAccountMappings = capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
|
productData.buydownFeeClassificationToIncomeAccountMappings = buydownFeeClassificationToIncomeAccountMappings;
|
||||||
return productData;
|
return productData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,6 +1046,10 @@ public class LoanProductData implements Serializable {
|
|||||||
this.buyDownFeeCalculationTypeOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeCalculationType.class);
|
this.buyDownFeeCalculationTypeOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeCalculationType.class);
|
||||||
this.buyDownFeeStrategyOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class);
|
this.buyDownFeeStrategyOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class);
|
||||||
this.buyDownFeeIncomeTypeOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class);
|
this.buyDownFeeIncomeTypeOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class);
|
||||||
|
this.capitalizedIncomeClassificationOptions = null;
|
||||||
|
this.buydownFeeClassificationOptions = null;
|
||||||
|
this.capitalizedIncomeClassificationToIncomeAccountMappings = null;
|
||||||
|
this.buydownFeeClassificationToIncomeAccountMappings = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||||
@@ -1065,7 +1079,8 @@ public class LoanProductData implements Serializable {
|
|||||||
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions,
|
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions,
|
||||||
final List<StringEnumOptionData> capitalizedIncomeTypeOptions,
|
final List<StringEnumOptionData> capitalizedIncomeTypeOptions,
|
||||||
final List<StringEnumOptionData> buyDownFeeCalculationTypeOptions, final List<StringEnumOptionData> buyDownFeeStrategyOptions,
|
final List<StringEnumOptionData> buyDownFeeCalculationTypeOptions, final List<StringEnumOptionData> buyDownFeeStrategyOptions,
|
||||||
final List<StringEnumOptionData> buyDownFeeIncomeTypeOptions) {
|
final List<StringEnumOptionData> buyDownFeeIncomeTypeOptions, final List<CodeValueData> capitalizedIncomeClassificationOptions,
|
||||||
|
final List<CodeValueData> buydownFeeClassificationOptions) {
|
||||||
|
|
||||||
this.id = productData.id;
|
this.id = productData.id;
|
||||||
this.name = productData.name;
|
this.name = productData.name;
|
||||||
@@ -1246,6 +1261,10 @@ public class LoanProductData implements Serializable {
|
|||||||
this.buyDownFeeIncomeTypeOptions = buyDownFeeIncomeTypeOptions;
|
this.buyDownFeeIncomeTypeOptions = buyDownFeeIncomeTypeOptions;
|
||||||
|
|
||||||
this.merchantBuyDownFee = productData.isMerchantBuyDownFee();
|
this.merchantBuyDownFee = productData.isMerchantBuyDownFee();
|
||||||
|
this.capitalizedIncomeClassificationOptions = capitalizedIncomeClassificationOptions;
|
||||||
|
this.buydownFeeClassificationOptions = buydownFeeClassificationOptions;
|
||||||
|
this.buydownFeeClassificationToIncomeAccountMappings = productData.buydownFeeClassificationToIncomeAccountMappings;
|
||||||
|
this.capitalizedIncomeClassificationToIncomeAccountMappings = productData.capitalizedIncomeClassificationToIncomeAccountMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||||
|
|||||||
+2
-7
@@ -18,30 +18,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanproduct.handler;
|
package org.apache.fineract.portfolio.loanproduct.handler;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import org.apache.fineract.commands.annotation.CommandType;
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanProductWritePlatformService;
|
import org.apache.fineract.portfolio.loanproduct.service.LoanProductWritePlatformService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
@CommandType(entity = "LOANPRODUCT", action = "CREATE")
|
@CommandType(entity = "LOANPRODUCT", action = "CREATE")
|
||||||
public class CreateLoanProductCommandHandler implements NewCommandSourceHandler {
|
public class CreateLoanProductCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final LoanProductWritePlatformService writePlatformService;
|
private final LoanProductWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public CreateLoanProductCommandHandler(final LoanProductWritePlatformService writePlatformService) {
|
|
||||||
this.writePlatformService = writePlatformService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
|
||||||
return this.writePlatformService.createLoanProduct(command);
|
return this.writePlatformService.createLoanProduct(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -48,4 +48,6 @@ public class LoanDTO {
|
|||||||
private Long chargeOffReasonCodeValue;
|
private Long chargeOffReasonCodeValue;
|
||||||
private boolean markedAsWrittenOff;
|
private boolean markedAsWrittenOff;
|
||||||
private boolean merchantBuyDownFee;
|
private boolean merchantBuyDownFee;
|
||||||
|
private List<AdvancedMappingtDTO> buydownFeeAdvancedMappingData;
|
||||||
|
private List<AdvancedMappingtDTO> capitalizedIncomeAdvancedMappingData;
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-1
@@ -35,6 +35,7 @@ import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsFor
|
|||||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccount;
|
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccount;
|
||||||
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
|
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
@@ -171,7 +172,9 @@ public class AccountingProcessorHelper {
|
|||||||
|
|
||||||
return new LoanDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, upfrontAccrualBasedAccountingEnabled,
|
return new LoanDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, upfrontAccrualBasedAccountingEnabled,
|
||||||
periodicAccrualBasedAccountingEnabled, newLoanTransactions, isLoanMarkedAsChargeOff, isLoanMarkedAsFraud,
|
periodicAccrualBasedAccountingEnabled, newLoanTransactions, isLoanMarkedAsChargeOff, isLoanMarkedAsFraud,
|
||||||
chargeOffReasonCodeValue, isLoanMarkedAsWrittenOff, merchantBuyDownFee);
|
chargeOffReasonCodeValue, isLoanMarkedAsWrittenOff, merchantBuyDownFee,
|
||||||
|
accountingBridgeData.getBuydownFeeClassificationCodeValue(),
|
||||||
|
accountingBridgeData.getCapitalizedIncomeClassificationCodeValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Long loanProductId, PortfolioProductType productType,
|
public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Long loanProductId, PortfolioProductType productType,
|
||||||
@@ -179,6 +182,16 @@ public class AccountingProcessorHelper {
|
|||||||
return accountMappingRepository.findChargeOffReasonMapping(loanProductId, productType.getValue(), chargeOffReasonId);
|
return accountMappingRepository.findChargeOffReasonMapping(loanProductId, productType.getValue(), chargeOffReasonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProductToGLAccountMapping getClassificationMappingByCodeValue(Long loanProductId, PortfolioProductType productType,
|
||||||
|
final Long classificationId, final String classificationType) {
|
||||||
|
if (LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue().equals(classificationType)) {
|
||||||
|
return accountMappingRepository.findBuydownFeeClassificationMapping(loanProductId, productType.getValue(), classificationId);
|
||||||
|
} else {
|
||||||
|
return accountMappingRepository.findCapitalizedIncomeClassificationMapping(loanProductId, productType.getValue(),
|
||||||
|
classificationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
||||||
final boolean accrualBasedAccountingEnabled) {
|
final boolean accrualBasedAccountingEnabled) {
|
||||||
final Long loanId = (Long) accountingBridgeData.get("savingsId");
|
final Long loanId = (Long) accountingBridgeData.get("savingsId");
|
||||||
@@ -472,6 +485,14 @@ public class AccountingProcessorHelper {
|
|||||||
transactionId, transactionDate, amount);
|
transactionId, transactionDate, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createJournalEntriesForLoan(final Office office, final String currencyCode, final Integer accountTypeToBeDebited,
|
||||||
|
final GLAccount accountToBeCredited, final Long loanProductId, final Long paymentTypeId, final Long loanId,
|
||||||
|
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
||||||
|
int accountTypeToDebitId = accountTypeToBeDebited;
|
||||||
|
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountToBeCredited, loanProductId, paymentTypeId, loanId,
|
||||||
|
transactionId, transactionDate, amount);
|
||||||
|
}
|
||||||
|
|
||||||
public void createSplitJournalEntriesForLoan(Office office, String currencyCode, List<JournalAmountHolder> splitAccountsHolder,
|
public void createSplitJournalEntriesForLoan(Office office, String currencyCode, List<JournalAmountHolder> splitAccountsHolder,
|
||||||
JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, Long loanId, String transactionId,
|
JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, Long loanId, String transactionId,
|
||||||
LocalDate transactionDate) {
|
LocalDate transactionDate) {
|
||||||
@@ -534,6 +555,14 @@ public class AccountingProcessorHelper {
|
|||||||
createCreditJournalEntryForLoan(office, currencyCode, creditAccount, loanId, transactionId, transactionDate, amount);
|
createCreditJournalEntryForLoan(office, currencyCode, creditAccount, loanId, transactionId, transactionDate, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createJournalEntriesForLoan(final Office office, final String currencyCode, final int accountTypeToDebitId,
|
||||||
|
final GLAccount creditAccount, final Long loanProductId, final Long paymentTypeId, final Long loanId,
|
||||||
|
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
||||||
|
final GLAccount debitAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToDebitId, paymentTypeId);
|
||||||
|
createDebitJournalEntryForLoan(office, currencyCode, debitAccount, loanId, transactionId, transactionDate, amount);
|
||||||
|
createCreditJournalEntryForLoan(office, currencyCode, creditAccount, loanId, transactionId, transactionDate, amount);
|
||||||
|
}
|
||||||
|
|
||||||
private void createJournalEntriesForSavings(final Office office, final String currencyCode, final int accountTypeToDebitId,
|
private void createJournalEntriesForSavings(final Office office, final String currencyCode, final int accountTypeToDebitId,
|
||||||
final int accountTypeToCreditId, final Long savingsProductId, final Long paymentTypeId, final Long savingsId,
|
final int accountTypeToCreditId, final Long savingsProductId, final Long paymentTypeId, final Long savingsId,
|
||||||
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
||||||
|
|||||||
+106
-8
@@ -29,7 +29,9 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
import org.apache.fineract.accounting.closure.domain.GLClosure;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
|
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
|
||||||
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.AdvancedMappingtDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.GLAccountBalanceHolder;
|
import org.apache.fineract.accounting.journalentry.data.GLAccountBalanceHolder;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||||
@@ -296,17 +298,57 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
|
final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
|
||||||
|
|
||||||
|
final List<AdvancedMappingtDTO> classificationCodeValues = loanDTO.getCapitalizedIncomeAdvancedMappingData();
|
||||||
|
|
||||||
// interest payment
|
// interest payment
|
||||||
final AccrualAccountsForLoan creditAccountType = isLoanWrittenOff ? AccrualAccountsForLoan.LOSSES_WRITTEN_OFF
|
final AccrualAccountsForLoan creditAccountType = isLoanWrittenOff ? AccrualAccountsForLoan.LOSSES_WRITTEN_OFF
|
||||||
: AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION;
|
: AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION;
|
||||||
if (MathUtil.isGreaterThanZero(interestAmount)) {
|
if (MathUtil.isGreaterThanZero(interestAmount)) {
|
||||||
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, creditAccountType.getValue(),
|
if (classificationCodeValues.isEmpty()) {
|
||||||
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, creditAccountType.getValue(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
classificationCodeValues.stream().forEach(classificationCodeValue -> {
|
||||||
|
ProductToGLAccountMapping mapping = null;
|
||||||
|
if (classificationCodeValue.getReferenceValueId() != null) {
|
||||||
|
mapping = fetchAdvanceAccountingMappingForCodeValue(loanProductId, classificationCodeValue.getReferenceValueId(),
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping == null) {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
|
||||||
|
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
|
||||||
|
glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// handle fees payment
|
// handle fees payment
|
||||||
if (MathUtil.isGreaterThanZero(feesAmount)) {
|
if (MathUtil.isGreaterThanZero(feesAmount)) {
|
||||||
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, creditAccountType.getValue(),
|
if (classificationCodeValues.isEmpty()) {
|
||||||
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, creditAccountType.getValue(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
classificationCodeValues.stream().forEach(classificationCodeValue -> {
|
||||||
|
ProductToGLAccountMapping mapping = null;
|
||||||
|
if (classificationCodeValue.getReferenceValueId() != null) {
|
||||||
|
mapping = fetchAdvanceAccountingMappingForCodeValue(loanProductId, classificationCodeValue.getReferenceValueId(),
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping == null) {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
|
||||||
|
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
|
||||||
|
glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create credit entries
|
// create credit entries
|
||||||
@@ -327,6 +369,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProductToGLAccountMapping fetchAdvanceAccountingMappingForCodeValue(final Long loanProductId, final Long codeValueId,
|
||||||
|
final String codeName) {
|
||||||
|
return helper.getClassificationMappingByCodeValue(loanProductId, PortfolioProductType.LOAN, codeValueId, codeName);
|
||||||
|
}
|
||||||
|
|
||||||
private void createJournalEntriesForChargeOffLoanCapitalizedIncomeAmortization(final LoanDTO loanDTO,
|
private void createJournalEntriesForChargeOffLoanCapitalizedIncomeAmortization(final LoanDTO loanDTO,
|
||||||
final LoanTransactionDTO loanTransactionDTO, final Office office) {
|
final LoanTransactionDTO loanTransactionDTO, final Office office) {
|
||||||
// loan properties
|
// loan properties
|
||||||
@@ -493,17 +540,57 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
|
||||||
final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
|
final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
|
||||||
|
|
||||||
|
final List<AdvancedMappingtDTO> classificationCodeValues = loanDTO.getBuydownFeeAdvancedMappingData();
|
||||||
|
|
||||||
// interest payment
|
// interest payment
|
||||||
final AccrualAccountsForLoan creditAccountType = isLoanWrittenOff ? AccrualAccountsForLoan.LOSSES_WRITTEN_OFF
|
final AccrualAccountsForLoan creditAccountType = isLoanWrittenOff ? AccrualAccountsForLoan.LOSSES_WRITTEN_OFF
|
||||||
: AccrualAccountsForLoan.INCOME_FROM_BUY_DOWN;
|
: AccrualAccountsForLoan.INCOME_FROM_BUY_DOWN;
|
||||||
if (MathUtil.isGreaterThanZero(interestAmount)) {
|
if (MathUtil.isGreaterThanZero(interestAmount)) {
|
||||||
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, creditAccountType.getValue(),
|
if (classificationCodeValues.isEmpty()) {
|
||||||
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, creditAccountType.getValue(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
classificationCodeValues.stream().forEach(classificationCodeValue -> {
|
||||||
|
ProductToGLAccountMapping mapping = null;
|
||||||
|
if (classificationCodeValue.getReferenceValueId() != null) {
|
||||||
|
mapping = fetchAdvanceAccountingMappingForCodeValue(loanProductId, classificationCodeValue.getReferenceValueId(),
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping == null) {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
|
||||||
|
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
|
||||||
|
glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// handle fees payment
|
// handle fees payment
|
||||||
if (MathUtil.isGreaterThanZero(feesAmount)) {
|
if (MathUtil.isGreaterThanZero(feesAmount)) {
|
||||||
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, creditAccountType.getValue(),
|
if (classificationCodeValues.isEmpty()) {
|
||||||
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, creditAccountType.getValue(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
classificationCodeValues.stream().forEach(classificationCodeValue -> {
|
||||||
|
ProductToGLAccountMapping mapping = null;
|
||||||
|
if (classificationCodeValue.getReferenceValueId() != null) {
|
||||||
|
mapping = fetchAdvanceAccountingMappingForCodeValue(loanProductId, classificationCodeValue.getReferenceValueId(),
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping == null) {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
|
||||||
|
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
|
||||||
|
glAccountBalanceHolder);
|
||||||
|
} else {
|
||||||
|
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
|
||||||
|
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create credit entries
|
// create credit entries
|
||||||
@@ -800,6 +887,17 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void populateCreditDebitMaps(Long loanProductId, BigDecimal transactionPartAmount, Long paymentTypeId, GLAccount accountCredit,
|
||||||
|
Integer debitAccountType, GLAccountBalanceHolder glAccountBalanceHolder) {
|
||||||
|
if (MathUtil.isGreaterThanZero(transactionPartAmount)) {
|
||||||
|
// Resolve Credit
|
||||||
|
glAccountBalanceHolder.addToCredit(accountCredit, transactionPartAmount);
|
||||||
|
// Resolve Debit
|
||||||
|
GLAccount accountDebit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, debitAccountType, paymentTypeId);
|
||||||
|
glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
||||||
final boolean isMarkedAsChargeOff = loanDTO.isMarkedAsChargeOff();
|
final boolean isMarkedAsChargeOff = loanDTO.isMarkedAsChargeOff();
|
||||||
if (isMarkedAsChargeOff) {
|
if (isMarkedAsChargeOff) {
|
||||||
|
|||||||
+36
-1
@@ -44,6 +44,7 @@ import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformSer
|
|||||||
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
|
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
|
||||||
import org.apache.fineract.accounting.journalentry.command.JournalEntryCommand;
|
import org.apache.fineract.accounting.journalentry.command.JournalEntryCommand;
|
||||||
import org.apache.fineract.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
|
import org.apache.fineract.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.AdvancedMappingtDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.ClientTransactionDTO;
|
import org.apache.fineract.accounting.journalentry.data.ClientTransactionDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||||
@@ -61,6 +62,7 @@ import org.apache.fineract.accounting.provisioning.domain.ProvisioningEntry;
|
|||||||
import org.apache.fineract.accounting.rule.domain.AccountingRule;
|
import org.apache.fineract.accounting.rule.domain.AccountingRule;
|
||||||
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepository;
|
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepository;
|
||||||
import org.apache.fineract.accounting.rule.exception.AccountingRuleNotFoundException;
|
import org.apache.fineract.accounting.rule.exception.AccountingRuleNotFoundException;
|
||||||
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
|
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
|
||||||
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
|
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
@@ -90,11 +92,14 @@ import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||||
@@ -128,6 +133,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
private final ConfigurationReadPlatformService configurationReadPlatformService;
|
private final ConfigurationReadPlatformService configurationReadPlatformService;
|
||||||
private final AccountingService accountingService;
|
private final AccountingService accountingService;
|
||||||
private final ExternalAssetOwnerRepository externalAssetOwnerRepository;
|
private final ExternalAssetOwnerRepository externalAssetOwnerRepository;
|
||||||
|
private final LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository;
|
||||||
|
private final LoanTransactionRepository loanTransactionRepository;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -857,11 +864,39 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<AdvancedMappingtDTO> buydownFeeAdvancedMappingData = null;
|
||||||
|
List<AdvancedMappingtDTO> capitalizedIncomeAdvancedMappingData = null;
|
||||||
|
if (loanTransaction.isBuyDownFeeAmortization()) {
|
||||||
|
buydownFeeAdvancedMappingData = getLoanTransactionClassificationId(loanTransaction);
|
||||||
|
} else if (loanTransaction.isCapitalizedIncomeAmortization()) {
|
||||||
|
capitalizedIncomeAdvancedMappingData = getLoanTransactionClassificationId(loanTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode,
|
return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode,
|
||||||
loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
||||||
isAccountTransfer, wasChargedOffAtTransactionTime, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(),
|
isAccountTransfer, wasChargedOffAtTransactionTime, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(),
|
||||||
transactions, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee());
|
transactions, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee(), buydownFeeAdvancedMappingData,
|
||||||
|
capitalizedIncomeAdvancedMappingData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AdvancedMappingtDTO> getLoanTransactionClassificationId(final LoanTransaction loanTransaction) {
|
||||||
|
List<AdvancedMappingtDTO> advancedMappingData = new ArrayList<AdvancedMappingtDTO>();
|
||||||
|
if (loanTransaction.isCapitalizedIncomeAmortization() || loanTransaction.isBuyDownFeeAmortization()) {
|
||||||
|
final List<LoanAmortizationAllocationMapping> loanTransactionAllocations = loanAmortizationAllocationMappingRepository
|
||||||
|
.fetchLoanTransactionAllocationByAmortizationLoanTransactionId(loanTransaction.getId(),
|
||||||
|
loanTransaction.getLoan().getId());
|
||||||
|
loanTransactionAllocations.stream().forEach(loanTransactionAllocation -> {
|
||||||
|
final CodeValue classification = loanTransactionRepository
|
||||||
|
.fetchClassificationCodeValueByTransactionId(loanTransactionAllocation.getBaseLoanTransactionId());
|
||||||
|
if (classification != null) {
|
||||||
|
advancedMappingData.add(new AdvancedMappingtDTO(classification.getId(), loanTransactionAllocation.getAmount()));
|
||||||
|
} else {
|
||||||
|
advancedMappingData.add(new AdvancedMappingtDTO(null, loanTransactionAllocation.getAmount()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return advancedMappingData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-2
@@ -49,6 +49,8 @@ import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
|||||||
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -94,12 +96,14 @@ public class AccountingJournalEntryConfiguration {
|
|||||||
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
|
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
|
||||||
CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions,
|
CashBasedAccountingProcessorForClientTransactions accountingProcessorForClientTransactions,
|
||||||
ConfigurationReadPlatformService configurationReadPlatformService, AccountingService accountingService,
|
ConfigurationReadPlatformService configurationReadPlatformService, AccountingService accountingService,
|
||||||
ExternalAssetOwnerRepository externalAssetOwnerRepository) {
|
ExternalAssetOwnerRepository externalAssetOwnerRepository,
|
||||||
|
LoanAmortizationAllocationMappingRepository loanAmortizationAllocationMappingRepository,
|
||||||
|
LoanTransactionRepository loanTransactionRepository) {
|
||||||
return new JournalEntryWritePlatformServiceJpaRepositoryImpl(glClosureRepository, glAccountRepository, glJournalEntryRepository,
|
return new JournalEntryWritePlatformServiceJpaRepositoryImpl(glClosureRepository, glAccountRepository, glJournalEntryRepository,
|
||||||
officeRepositoryWrapper, accountingProcessorForLoanFactory, accountingProcessorForSavingsFactory,
|
officeRepositoryWrapper, accountingProcessorForLoanFactory, accountingProcessorForSavingsFactory,
|
||||||
accountingProcessorForSharesFactory, helper, fromApiJsonDeserializer, accountingRuleRepository,
|
accountingProcessorForSharesFactory, helper, fromApiJsonDeserializer, accountingRuleRepository,
|
||||||
glAccountReadPlatformService, organisationCurrencyRepository, context, paymentDetailWritePlatformService,
|
glAccountReadPlatformService, organisationCurrencyRepository, context, paymentDetailWritePlatformService,
|
||||||
financialActivityAccountRepositoryWrapper, accountingProcessorForClientTransactions, configurationReadPlatformService,
|
financialActivityAccountRepositoryWrapper, accountingProcessorForClientTransactions, configurationReadPlatformService,
|
||||||
accountingService, externalAssetOwnerRepository);
|
accountingService, externalAssetOwnerRepository, loanAmortizationAllocationMappingRepository, loanTransactionRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -140,6 +140,10 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
||||||
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
||||||
this.loanProductToGLAccountMappingHelper.saveChargeOffReasonToExpenseAccountMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.saveChargeOffReasonToExpenseAccountMappings(command, element, loanProductId, null);
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveBuyDownFeeClassificationToIncomeAccountMappings(command, element,
|
||||||
|
loanProductId, null);
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveCapitalizedIncomeClassificationToIncomeAccountMappings(command, element,
|
||||||
|
loanProductId, null);
|
||||||
break;
|
break;
|
||||||
case ACCRUAL_UPFRONT:
|
case ACCRUAL_UPFRONT:
|
||||||
// Fall Through
|
// Fall Through
|
||||||
@@ -233,6 +237,10 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
|
||||||
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
|
||||||
this.loanProductToGLAccountMappingHelper.saveChargeOffReasonToExpenseAccountMappings(command, element, loanProductId, null);
|
this.loanProductToGLAccountMappingHelper.saveChargeOffReasonToExpenseAccountMappings(command, element, loanProductId, null);
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveBuyDownFeeClassificationToIncomeAccountMappings(command, element,
|
||||||
|
loanProductId, null);
|
||||||
|
this.loanProductToGLAccountMappingHelper.saveCapitalizedIncomeClassificationToIncomeAccountMappings(command, element,
|
||||||
|
loanProductId, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,6 +419,10 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
|
|||||||
this.loanProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, loanProductId, changes);
|
this.loanProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, loanProductId, changes);
|
||||||
this.loanProductToGLAccountMappingHelper.updateChargeOffReasonToExpenseAccountMappings(command, element, loanProductId,
|
this.loanProductToGLAccountMappingHelper.updateChargeOffReasonToExpenseAccountMappings(command, element, loanProductId,
|
||||||
changes);
|
changes);
|
||||||
|
this.loanProductToGLAccountMappingHelper.updateBuyDownFeeClassificationToIncomeAccountMappings(command, element, loanProductId,
|
||||||
|
changes);
|
||||||
|
this.loanProductToGLAccountMappingHelper.updateCapitalizedIncomeClassificationToIncomeAccountMappings(command, element,
|
||||||
|
loanProductId, changes);
|
||||||
}
|
}
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -46,10 +46,12 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
|
||||||
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
|
||||||
|
import org.apache.fineract.accounting.producttoaccountmapping.data.ClassificationToGLAccountData;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
|
||||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService;
|
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingReadPlatformService;
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
@@ -82,6 +84,7 @@ import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlat
|
|||||||
import org.apache.fineract.portfolio.fund.data.FundData;
|
import org.apache.fineract.portfolio.fund.data.FundData;
|
||||||
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
|
||||||
@@ -352,6 +355,8 @@ public class LoanProductsApiResource {
|
|||||||
Collection<ChargeToGLAccountMapper> feeToGLAccountMappings;
|
Collection<ChargeToGLAccountMapper> feeToGLAccountMappings;
|
||||||
Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings;
|
Collection<ChargeToGLAccountMapper> penaltyToGLAccountMappings;
|
||||||
List<ChargeOffReasonToGLAccountMapper> chargeOffReasonToGLAccountMappings;
|
List<ChargeOffReasonToGLAccountMapper> chargeOffReasonToGLAccountMappings;
|
||||||
|
List<ClassificationToGLAccountData> capitalizedIncomeClassificationToGLAccountMappings;
|
||||||
|
List<ClassificationToGLAccountData> buydowFeeClassificationToGLAccountMappings;
|
||||||
if (loanProduct.hasAccountingEnabled()) {
|
if (loanProduct.hasAccountingEnabled()) {
|
||||||
accountingMappings = this.accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId,
|
accountingMappings = this.accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId,
|
||||||
loanProduct.getAccountingRule().getId().intValue());
|
loanProduct.getAccountingRule().getId().intValue());
|
||||||
@@ -362,8 +367,14 @@ public class LoanProductsApiResource {
|
|||||||
.fetchPenaltyToIncomeAccountMappingsForLoanProduct(productId);
|
.fetchPenaltyToIncomeAccountMappingsForLoanProduct(productId);
|
||||||
chargeOffReasonToGLAccountMappings = this.accountMappingReadPlatformService
|
chargeOffReasonToGLAccountMappings = this.accountMappingReadPlatformService
|
||||||
.fetchChargeOffReasonMappingsForLoanProduct(productId);
|
.fetchChargeOffReasonMappingsForLoanProduct(productId);
|
||||||
|
capitalizedIncomeClassificationToGLAccountMappings = accountMappingReadPlatformService
|
||||||
|
.fetchClassificationMappingsForLoanProduct(productId,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
buydowFeeClassificationToGLAccountMappings = accountMappingReadPlatformService.fetchClassificationMappingsForLoanProduct(
|
||||||
|
productId, LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
loanProduct = LoanProductData.withAccountingDetails(loanProduct, accountingMappings, paymentChannelToFundSourceMappings,
|
loanProduct = LoanProductData.withAccountingDetails(loanProduct, accountingMappings, paymentChannelToFundSourceMappings,
|
||||||
feeToGLAccountMappings, penaltyToGLAccountMappings, chargeOffReasonToGLAccountMappings);
|
feeToGLAccountMappings, penaltyToGLAccountMappings, chargeOffReasonToGLAccountMappings,
|
||||||
|
capitalizedIncomeClassificationToGLAccountMappings, buydowFeeClassificationToGLAccountMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.isTemplate()) {
|
if (settings.isTemplate()) {
|
||||||
@@ -464,6 +475,10 @@ public class LoanProductsApiResource {
|
|||||||
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class);
|
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class);
|
||||||
final List<StringEnumOptionData> buyDownFeeIncomeTypeOptions = ApiFacingEnum
|
final List<StringEnumOptionData> buyDownFeeIncomeTypeOptions = ApiFacingEnum
|
||||||
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class);
|
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class);
|
||||||
|
final List<CodeValueData> capitalizedIncomeClassificationOptions = codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE);
|
||||||
|
final List<CodeValueData> buydownFeeClassificationOptions = codeValueReadPlatformService
|
||||||
|
.retrieveCodeValuesByCode(LoanTransactionApiConstants.BUY_DOWN_FEE_CLASSIFICATION_CODE);
|
||||||
|
|
||||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
|
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
|
||||||
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
|
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
|
||||||
@@ -477,7 +492,8 @@ public class LoanProductsApiResource {
|
|||||||
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
|
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
|
||||||
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
|
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
|
||||||
daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions,
|
daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions,
|
||||||
capitalizedIncomeTypeOptions, buyDownFeeCalculationTypeOptions, buyDownFeeStrategyOptions, buyDownFeeIncomeTypeOptions);
|
capitalizedIncomeTypeOptions, buyDownFeeCalculationTypeOptions, buyDownFeeStrategyOptions, buyDownFeeIncomeTypeOptions,
|
||||||
|
capitalizedIncomeClassificationOptions, buydownFeeClassificationOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-2
@@ -49,6 +49,7 @@ import org.apache.fineract.infrastructure.core.service.MathUtil;
|
|||||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
|
||||||
@@ -202,7 +203,10 @@ public final class LoanProductDataValidator {
|
|||||||
LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME,
|
LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME,
|
||||||
LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME,
|
LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME,
|
||||||
LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(),
|
LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(),
|
||||||
LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME));
|
LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue(), //
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue() //
|
||||||
|
));
|
||||||
|
|
||||||
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
|
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
|
||||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
|
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
|
||||||
@@ -744,7 +748,10 @@ public final class LoanProductDataValidator {
|
|||||||
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
||||||
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
||||||
validateChargeOffToExpenseMappings(baseDataValidator, element);
|
validateChargeOffToExpenseMappings(baseDataValidator, element);
|
||||||
|
validateClassificationToIncomeMappings(baseDataValidator, element,
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
validateClassificationToIncomeMappings(baseDataValidator, element,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
|
if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
|
||||||
@@ -1877,6 +1884,10 @@ public final class LoanProductDataValidator {
|
|||||||
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
|
||||||
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
validateChargeToIncomeAccountMappings(baseDataValidator, element);
|
||||||
validateChargeOffToExpenseMappings(baseDataValidator, element);
|
validateChargeOffToExpenseMappings(baseDataValidator, element);
|
||||||
|
validateClassificationToIncomeMappings(baseDataValidator, element,
|
||||||
|
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
validateClassificationToIncomeMappings(baseDataValidator, element,
|
||||||
|
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS);
|
||||||
|
|
||||||
validateMinMaxConstraints(element, baseDataValidator, loanProduct);
|
validateMinMaxConstraints(element, baseDataValidator, loanProduct);
|
||||||
|
|
||||||
@@ -2109,6 +2120,65 @@ public final class LoanProductDataValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateClassificationToIncomeMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element,
|
||||||
|
final LoanProductAccountingParams classificationParameter) {
|
||||||
|
String parameterName = classificationParameter.getValue();
|
||||||
|
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(parameterName, element)) {
|
||||||
|
final JsonArray classificationToIncomeMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, element);
|
||||||
|
if (classificationToIncomeMappingArray != null && classificationToIncomeMappingArray.size() > 0) {
|
||||||
|
Map<Long, Set<Long>> classificationToAccounts = new HashMap<>();
|
||||||
|
List<JsonObject> processedMappings = new ArrayList<>(); // Collect processed mappings for the new method
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
do {
|
||||||
|
final JsonObject jsonObject = classificationToIncomeMappingArray.get(i).getAsJsonObject();
|
||||||
|
final Long incomeGlAccountId = this.fromApiJsonHelper
|
||||||
|
.extractLongNamed(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(), jsonObject);
|
||||||
|
final Long classificationCodeValueId = this.fromApiJsonHelper
|
||||||
|
.extractLongNamed(LoanProductAccountingParams.CLASSIFICATION_CODE_VALUE_ID.getValue(), jsonObject);
|
||||||
|
|
||||||
|
// Validate parameters locally
|
||||||
|
baseDataValidator.reset()
|
||||||
|
.parameter(parameterName + OPENING_SQUARE_BRACKET + i + CLOSING_SQUARE_BRACKET + DOT
|
||||||
|
+ LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue())
|
||||||
|
.value(incomeGlAccountId).notNull().integerGreaterThanZero();
|
||||||
|
baseDataValidator.reset()
|
||||||
|
.parameter(parameterName + OPENING_SQUARE_BRACKET + i + CLOSING_SQUARE_BRACKET + DOT
|
||||||
|
+ LoanProductAccountingParams.CLASSIFICATION_CODE_VALUE_ID.getValue())
|
||||||
|
.value(classificationCodeValueId).notNull().integerGreaterThanZero();
|
||||||
|
|
||||||
|
// Handle duplicate classification and GL Account validation
|
||||||
|
classificationToAccounts.putIfAbsent(classificationCodeValueId, new HashSet<>());
|
||||||
|
Set<Long> associatedAccounts = classificationToAccounts.get(classificationCodeValueId);
|
||||||
|
|
||||||
|
if (associatedAccounts.contains(incomeGlAccountId)) {
|
||||||
|
baseDataValidator.reset().parameter(parameterName + OPENING_SQUARE_BRACKET + i + CLOSING_SQUARE_BRACKET)
|
||||||
|
.failWithCode("duplicate.classification.and.glAccount");
|
||||||
|
}
|
||||||
|
associatedAccounts.add(incomeGlAccountId);
|
||||||
|
|
||||||
|
if (associatedAccounts.size() > 1) {
|
||||||
|
baseDataValidator.reset().parameter(parameterName + OPENING_SQUARE_BRACKET + i + CLOSING_SQUARE_BRACKET)
|
||||||
|
.failWithCode("multiple.glAccounts.for.classification");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect mapping for additional validations
|
||||||
|
processedMappings.add(jsonObject);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
} while (i < classificationToIncomeMappingArray.size());
|
||||||
|
|
||||||
|
// Call the new validation method for additional checks
|
||||||
|
final String dataCodeName = classificationParameter
|
||||||
|
.equals(LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS)
|
||||||
|
? LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE
|
||||||
|
: LoanTransactionApiConstants.BUY_DOWN_FEE_CLASSIFICATION_CODE;
|
||||||
|
productToGLAccountMappingHelper.validateClassificationMappingsInDatabase(processedMappings, dataCodeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator,
|
public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator,
|
||||||
final LoanProduct loanProduct) {
|
final LoanProduct loanProduct) {
|
||||||
validatePrincipalMinMaxConstraint(element, loanProduct, baseDataValidator);
|
validatePrincipalMinMaxConstraint(element, loanProduct, baseDataValidator);
|
||||||
|
|||||||
@@ -216,4 +216,5 @@
|
|||||||
<include file="parts/0195_create_loan_amortization_allocation_mapping.xml" relativeToChangelogFile="true" />
|
<include file="parts/0195_create_loan_amortization_allocation_mapping.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0196_add_deleted_and_closed_to_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
<include file="parts/0196_add_deleted_and_closed_to_buy_down_fee_balance.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0197_add_deleted_and_closed_to_capitalized_income_balance.xml" relativeToChangelogFile="true" />
|
<include file="parts/0197_add_deleted_and_closed_to_capitalized_income_balance.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0198_add_classification_id_to_acc_product_mapping.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
|
||||||
|
<changeSet id="1" author="fineract">
|
||||||
|
<addColumn tableName="acc_product_mapping">
|
||||||
|
<column name="capitalized_income_classification_id" type="INT" defaultValueNumeric="NULL">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
<addForeignKeyConstraint
|
||||||
|
baseTableName="acc_product_mapping"
|
||||||
|
baseColumnNames="capitalized_income_classification_id"
|
||||||
|
referencedTableName="m_code_value"
|
||||||
|
referencedColumnNames="id"
|
||||||
|
constraintName="fk_acc_product_mapping_capitalized_income_classification"/>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="2" author="fineract">
|
||||||
|
<addColumn tableName="acc_product_mapping">
|
||||||
|
<column name="buydown_fee_classification_id" type="INT" defaultValueNumeric="NULL">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
<addForeignKeyConstraint
|
||||||
|
baseTableName="acc_product_mapping"
|
||||||
|
baseColumnNames="buydown_fee_classification_id"
|
||||||
|
referencedTableName="m_code_value"
|
||||||
|
referencedColumnNames="id"
|
||||||
|
constraintName="fk_acc_product_mapping_buydown_fee_classification"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+1
-1
@@ -73,7 +73,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
|
|||||||
Collections.emptyList(), false, "", null, null, null, null);
|
Collections.emptyList(), false, "", null, null, null, null);
|
||||||
|
|
||||||
loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasonId, false,
|
loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasonId, false,
|
||||||
false);
|
false, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+123
-11
@@ -28,28 +28,38 @@ import java.math.BigDecimal;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.client.models.BuyDownFeeAmortizationDetails;
|
import org.apache.fineract.client.models.BuyDownFeeAmortizationDetails;
|
||||||
|
import org.apache.fineract.client.models.GetCodesResponse;
|
||||||
|
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
||||||
|
import org.apache.fineract.client.models.PostClassificationToIncomeAccountMappings;
|
||||||
import org.apache.fineract.client.models.PostClientsResponse;
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
|
import org.apache.fineract.client.models.PostCodeValueDataResponse;
|
||||||
|
import org.apache.fineract.client.models.PostCodeValuesDataRequest;
|
||||||
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
|
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||||
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.BusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.BusinessEvent;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
|
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionMinimalBusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionMinimalBusinessEvent;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@@ -88,7 +98,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
runAt("01 September 2024", () -> {
|
runAt("01 September 2024", () -> {
|
||||||
clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee());
|
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee(null));
|
||||||
|
|
||||||
// Apply for the loan with proper progressive loan settings
|
// Apply for the loan with proper progressive loan settings
|
||||||
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
||||||
@@ -240,14 +250,16 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
/**
|
/**
|
||||||
* Creates a progressive loan product with buy down fee enabled
|
* Creates a progressive loan product with buy down fee enabled
|
||||||
*/
|
*/
|
||||||
private PostLoanProductsRequest createProgressiveLoanProductWithBuyDownFee() {
|
private PostLoanProductsRequest createProgressiveLoanProductWithBuyDownFee(
|
||||||
|
PostClassificationToIncomeAccountMappings buydownFeeClassificationAccountMappings) {
|
||||||
// Create a progressive loan product with accrual-based accounting and proper GL mappings
|
// Create a progressive loan product with accrual-based accounting and proper GL mappings
|
||||||
return new PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("BUY_DOWN_FEE_PROGRESSIVE_", 6))
|
PostLoanProductsRequest postLoanProductsRequest = new PostLoanProductsRequest()
|
||||||
.shortName(Utils.uniqueRandomStringGenerator("", 4)).description("Progressive loan product with buy down fee enabled")
|
.name(Utils.uniqueRandomStringGenerator("BUY_DOWN_FEE_PROGRESSIVE_", 6)).shortName(Utils.uniqueRandomStringGenerator("", 4))
|
||||||
.includeInBorrowerCycle(false).useBorrowerCycle(false).currencyCode("USD").digitsAfterDecimal(2).principal(1000.0)
|
.description("Progressive loan product with buy down fee enabled").includeInBorrowerCycle(false).useBorrowerCycle(false)
|
||||||
.minPrincipal(100.0).maxPrincipal(10000.0).numberOfRepayments(12).minNumberOfRepayments(6).maxNumberOfRepayments(24)
|
.currencyCode("USD").digitsAfterDecimal(2).principal(1000.0).minPrincipal(100.0).maxPrincipal(10000.0)
|
||||||
.repaymentEvery(1).repaymentFrequencyType(RepaymentFrequencyType.MONTHS_L).interestRatePerPeriod(10.0)
|
.numberOfRepayments(12).minNumberOfRepayments(6).maxNumberOfRepayments(24).repaymentEvery(1)
|
||||||
.minInterestRatePerPeriod(0.0).maxInterestRatePerPeriod(120.0).interestRateFrequencyType(InterestRateFrequencyType.YEARS)
|
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS_L).interestRatePerPeriod(10.0).minInterestRatePerPeriod(0.0)
|
||||||
|
.maxInterestRatePerPeriod(120.0).interestRateFrequencyType(InterestRateFrequencyType.YEARS)
|
||||||
.amortizationType(AmortizationType.EQUAL_INSTALLMENTS).interestType(InterestType.DECLINING_BALANCE)
|
.amortizationType(AmortizationType.EQUAL_INSTALLMENTS).interestType(InterestType.DECLINING_BALANCE)
|
||||||
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY).allowPartialPeriodInterestCalcualtion(false)
|
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY).allowPartialPeriodInterestCalcualtion(false)
|
||||||
.transactionProcessingStrategyCode("advanced-payment-allocation-strategy")
|
.transactionProcessingStrategyCode("advanced-payment-allocation-strategy")
|
||||||
@@ -275,6 +287,11 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
.buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT)
|
.buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT)
|
||||||
.buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION)
|
.buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION)
|
||||||
.buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).locale("en").dateFormat("dd MMMM yyyy");
|
.buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).locale("en").dateFormat("dd MMMM yyyy");
|
||||||
|
|
||||||
|
if (buydownFeeClassificationAccountMappings != null) {
|
||||||
|
postLoanProductsRequest.addBuydownfeeClassificationToIncomeAccountMappingsItem(buydownFeeClassificationAccountMappings);
|
||||||
|
}
|
||||||
|
return postLoanProductsRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -485,6 +502,15 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
return response.getResourceId();
|
return response.getResourceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Long addBuyDownFeeForLoan(Long loanId, Double amount, String date, Long classificationId) {
|
||||||
|
String buyDownFeeExternalId = UUID.randomUUID().toString();
|
||||||
|
PostLoansLoanIdTransactionsResponse response = loanTransactionHelper.makeLoanBuyDownFee(loanId,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate(date).locale("en")
|
||||||
|
.transactionAmount(amount).externalId(buyDownFeeExternalId).note("Buy Down Fee Transaction")
|
||||||
|
.classificationId(classificationId));
|
||||||
|
return response.getResourceId();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBuyDownFeeDailyAmortization() {
|
public void testBuyDownFeeDailyAmortization() {
|
||||||
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
@@ -645,7 +671,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRetrieveBuyDownFeeAmortizationDetails() {
|
public void testRetrieveBuyDownFeeAmortizationDetails() {
|
||||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||||
final PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(createProgressiveLoanProductWithBuyDownFee());
|
final PostLoanProductsResponse loanProduct = loanProductHelper.createLoanProduct(createProgressiveLoanProductWithBuyDownFee(null));
|
||||||
|
|
||||||
final long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 February 2024", 1000.0,
|
final long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 February 2024", 1000.0,
|
||||||
7.0, 6, null);
|
7.0, 6, null);
|
||||||
@@ -677,7 +703,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
public void testRetrieveBuyDownFeeAmortizationDetails_notEnabled() {
|
public void testRetrieveBuyDownFeeAmortizationDetails_notEnabled() {
|
||||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||||
final PostLoanProductsResponse loanProduct = loanProductHelper
|
final PostLoanProductsResponse loanProduct = loanProductHelper
|
||||||
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee().enableBuyDownFee(false));
|
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee(null).enableBuyDownFee(false));
|
||||||
|
|
||||||
final long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 February 2024", 1000.0,
|
final long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 February 2024", 1000.0,
|
||||||
7.0, 6, null);
|
7.0, 6, null);
|
||||||
@@ -872,7 +898,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
// Add initial buy down fee
|
// Add initial buy down fee
|
||||||
|
|
||||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee().merchantBuyDownFee(false));
|
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee(null).merchantBuyDownFee(false));
|
||||||
|
|
||||||
// Apply for the loan with proper progressive loan settings
|
// Apply for the loan with proper progressive loan settings
|
||||||
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
||||||
@@ -917,4 +943,90 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuyDownFeeWithAdvanceAccountingMappings() {
|
||||||
|
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
|
final AtomicReference<Long> classificationIdRef = new AtomicReference<>();
|
||||||
|
final AtomicReference<Account> classificationIncomeAccountRef = new AtomicReference<>();
|
||||||
|
runAt("10 September 2024", () -> {
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
|
||||||
|
final AccountHelper accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||||
|
final Account classificationIncomeAccount = accountHelper
|
||||||
|
.createIncomeAccount(Utils.uniqueRandomStringGenerator("buydownfee_class_income_", 6));
|
||||||
|
classificationIncomeAccountRef.set(classificationIncomeAccount);
|
||||||
|
|
||||||
|
final GetCodesResponse code = codeHelper.retrieveCodeByName(LoanTransactionApiConstants.BUY_DOWN_FEE_CLASSIFICATION_CODE);
|
||||||
|
final PostCodeValueDataResponse classificationCode = codeHelper.createCodeValue(code.getId(),
|
||||||
|
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("CLASS_", 6)).isActive(true).position(10));
|
||||||
|
classificationIdRef.set(classificationCode.getSubResourceId());
|
||||||
|
|
||||||
|
// Loan Product create
|
||||||
|
final PostClassificationToIncomeAccountMappings classificationToIncomeMapping = new PostClassificationToIncomeAccountMappings()
|
||||||
|
.classificationCodeValueId(classificationIdRef.get())
|
||||||
|
.incomeAccountId(classificationIncomeAccount.getAccountID().longValue());
|
||||||
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
|
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee(classificationToIncomeMapping));
|
||||||
|
|
||||||
|
GetLoanProductsProductIdResponse getLoanProductResponse = loanProductHelper
|
||||||
|
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||||
|
assertNotNull(getLoanProductResponse);
|
||||||
|
assertNotNull(getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings());
|
||||||
|
Assertions.assertEquals(1, getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings().size());
|
||||||
|
Assertions.assertEquals(classificationIdRef.get(), getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings()
|
||||||
|
.get(0).getClassificationCodeValue().getId());
|
||||||
|
|
||||||
|
final PostCodeValueDataResponse secClassificationCode = codeHelper.createCodeValue(code.getId(),
|
||||||
|
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("CLASS_", 6)).isActive(true).position(10));
|
||||||
|
classificationIdRef.set(secClassificationCode.getSubResourceId());
|
||||||
|
|
||||||
|
// Loan Product update
|
||||||
|
final PutLoanProductsProductIdRequest putLoanProductRequest = new PutLoanProductsProductIdRequest();
|
||||||
|
putLoanProductRequest.addBuydownfeeClassificationToIncomeAccountMappingsItem(
|
||||||
|
new PostClassificationToIncomeAccountMappings().classificationCodeValueId(classificationIdRef.get())
|
||||||
|
.incomeAccountId(classificationIncomeAccount.getAccountID().longValue()));
|
||||||
|
|
||||||
|
loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(), putLoanProductRequest);
|
||||||
|
getLoanProductResponse = loanProductHelper.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||||
|
assertNotNull(getLoanProductResponse);
|
||||||
|
assertNotNull(getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings());
|
||||||
|
Assertions.assertEquals(1, getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings().size());
|
||||||
|
Assertions.assertEquals(classificationIdRef.get(), getLoanProductResponse.getBuydownFeeClassificationToIncomeAccountMappings()
|
||||||
|
.get(0).getClassificationCodeValue().getId());
|
||||||
|
|
||||||
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
||||||
|
loanProductsResponse.getResourceId(), "10 September 2024", 1000.0, 10.0, 12, null));
|
||||||
|
loanId = postLoansResponse.getLoanId();
|
||||||
|
loanIdRef.set(loanId);
|
||||||
|
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "10 September 2024"));
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1000.0), "10 September 2024");
|
||||||
|
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 400.0, "10 September 2024", classificationIdRef.get());
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("20 September 2024", () -> {
|
||||||
|
Long loanId = loanIdRef.get();
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
executeInlineCOB(loanId);
|
||||||
|
|
||||||
|
Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 50.0, "20 September 2024");
|
||||||
|
assertNotNull(buyDownFeeTransactionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("30 September 2024", () -> {
|
||||||
|
Long loanId = loanIdRef.get();
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
executeInlineCOB(loanId);
|
||||||
|
|
||||||
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
final Optional<GetLoansLoanIdTransactions> optTx = loanDetails.getTransactions().stream()
|
||||||
|
.filter(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), 1.23)
|
||||||
|
&& Objects.equals(item.getType().getValue(), "Buy Down Fee Amortization"))
|
||||||
|
.findFirst();
|
||||||
|
verifyTRJournalEntries(optTx.get().getId(), debit(deferredIncomeLiabilityAccount, 1.23),
|
||||||
|
credit(classificationIncomeAccountRef.get(), 1.09), // First BuyDown Fee With classification
|
||||||
|
credit(feeIncomeAccount, 0.14)); // Second BuyDown Fee Without classification
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+105
@@ -26,14 +26,17 @@ import java.math.BigDecimal;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.apache.fineract.client.models.CapitalizedIncomeDetails;
|
import org.apache.fineract.client.models.CapitalizedIncomeDetails;
|
||||||
import org.apache.fineract.client.models.GetCodesResponse;
|
import org.apache.fineract.client.models.GetCodesResponse;
|
||||||
|
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
||||||
import org.apache.fineract.client.models.LoanCapitalizedIncomeData;
|
import org.apache.fineract.client.models.LoanCapitalizedIncomeData;
|
||||||
|
import org.apache.fineract.client.models.PostClassificationToIncomeAccountMappings;
|
||||||
import org.apache.fineract.client.models.PostClientsResponse;
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
import org.apache.fineract.client.models.PostCodeValueDataResponse;
|
import org.apache.fineract.client.models.PostCodeValueDataResponse;
|
||||||
import org.apache.fineract.client.models.PostCodeValuesDataRequest;
|
import org.apache.fineract.client.models.PostCodeValuesDataRequest;
|
||||||
@@ -42,10 +45,13 @@ import org.apache.fineract.client.models.PostLoanProductsResponse;
|
|||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
|
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||||
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.LoanBusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.LoanBusinessEvent;
|
||||||
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionBusinessEvent;
|
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionBusinessEvent;
|
||||||
@@ -1179,4 +1185,103 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCapitalizedIncomeWithAdvanceAccountingMappings() {
|
||||||
|
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
|
final AtomicReference<Long> classificationIdRef = new AtomicReference<>();
|
||||||
|
final AtomicReference<Account> classificationIncomeAccountRef = new AtomicReference<>();
|
||||||
|
runAt("10 September 2024", () -> {
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||||
|
|
||||||
|
final AccountHelper accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||||
|
final Account classificationIncomeAccount = accountHelper
|
||||||
|
.createIncomeAccount(Utils.uniqueRandomStringGenerator("capitalizedincome_class_income_", 6));
|
||||||
|
classificationIncomeAccountRef.set(classificationIncomeAccount);
|
||||||
|
|
||||||
|
final GetCodesResponse code = codeHelper.retrieveCodeByName(LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE);
|
||||||
|
final PostCodeValueDataResponse classificationCode = codeHelper.createCodeValue(code.getId(),
|
||||||
|
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("CLASS_", 6)).isActive(true).position(10));
|
||||||
|
classificationIdRef.set(classificationCode.getSubResourceId());
|
||||||
|
|
||||||
|
// Loan Product create
|
||||||
|
final PostClassificationToIncomeAccountMappings classificationToIncomeMapping = new PostClassificationToIncomeAccountMappings()
|
||||||
|
.classificationCodeValueId(classificationIdRef.get())
|
||||||
|
.incomeAccountId(classificationIncomeAccount.getAccountID().longValue());
|
||||||
|
|
||||||
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
|
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
|
||||||
|
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||||
|
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
|
||||||
|
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
|
||||||
|
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
|
||||||
|
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)
|
||||||
|
.addCapitalizedIncomeClassificationToIncomeAccountMappingsItem(classificationToIncomeMapping));
|
||||||
|
|
||||||
|
GetLoanProductsProductIdResponse getLoanProductResponse = loanProductHelper
|
||||||
|
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||||
|
assertNotNull(getLoanProductResponse);
|
||||||
|
assertNotNull(getLoanProductResponse.getCapitalizedIncomeClassificationToIncomeAccountMappings());
|
||||||
|
Assertions.assertEquals(1, getLoanProductResponse.getCapitalizedIncomeClassificationToIncomeAccountMappings().size());
|
||||||
|
Assertions.assertEquals(classificationIdRef.get(), getLoanProductResponse
|
||||||
|
.getCapitalizedIncomeClassificationToIncomeAccountMappings().get(0).getClassificationCodeValue().getId());
|
||||||
|
|
||||||
|
final PostCodeValueDataResponse secClassificationCode = codeHelper.createCodeValue(code.getId(),
|
||||||
|
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("CLASS_", 6)).isActive(true).position(10));
|
||||||
|
classificationIdRef.set(secClassificationCode.getSubResourceId());
|
||||||
|
|
||||||
|
// Loan Product update
|
||||||
|
final PutLoanProductsProductIdRequest putLoanProductRequest = new PutLoanProductsProductIdRequest();
|
||||||
|
putLoanProductRequest.addCapitalizedIncomeClassificationToIncomeAccountMappingsItem(
|
||||||
|
new PostClassificationToIncomeAccountMappings().classificationCodeValueId(classificationIdRef.get())
|
||||||
|
.incomeAccountId(classificationIncomeAccount.getAccountID().longValue()));
|
||||||
|
|
||||||
|
loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(), putLoanProductRequest);
|
||||||
|
getLoanProductResponse = loanProductHelper.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||||
|
assertNotNull(getLoanProductResponse);
|
||||||
|
assertNotNull(getLoanProductResponse.getCapitalizedIncomeClassificationToIncomeAccountMappings());
|
||||||
|
Assertions.assertEquals(1, getLoanProductResponse.getCapitalizedIncomeClassificationToIncomeAccountMappings().size());
|
||||||
|
Assertions.assertEquals(classificationIdRef.get(), getLoanProductResponse
|
||||||
|
.getCapitalizedIncomeClassificationToIncomeAccountMappings().get(0).getClassificationCodeValue().getId());
|
||||||
|
|
||||||
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(client.getClientId(),
|
||||||
|
loanProductsResponse.getResourceId(), "10 September 2024", 1000.0, 10.0, 12, null));
|
||||||
|
Long loanId = postLoansResponse.getLoanId();
|
||||||
|
loanIdRef.set(loanId);
|
||||||
|
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "10 September 2024"));
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1000.0), "10 September 2024");
|
||||||
|
|
||||||
|
Long capitalizedIncomeTransactionId = loanTransactionHelper
|
||||||
|
.addCapitalizedIncome(loanId, "10 September 2024", 100.0, classificationIdRef.get()).getResourceId();
|
||||||
|
assertNotNull(capitalizedIncomeTransactionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("20 September 2024", () -> {
|
||||||
|
Long loanId = loanIdRef.get();
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
executeInlineCOB(loanId);
|
||||||
|
|
||||||
|
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "20 September 2024", 20.0)
|
||||||
|
.getResourceId();
|
||||||
|
assertNotNull(capitalizedIncomeTransactionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("30 September 2024", () -> {
|
||||||
|
Long loanId = loanIdRef.get();
|
||||||
|
deleteAllExternalEvents();
|
||||||
|
executeInlineCOB(loanId);
|
||||||
|
|
||||||
|
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
final Optional<GetLoansLoanIdTransactions> optTx = loanDetails.getTransactions().stream()
|
||||||
|
.filter(item -> Objects.equals(Utils.getDoubleValue(item.getAmount()), 0.33)
|
||||||
|
&& Objects.equals(item.getType().getValue(), "Capitalized Income Amortization"))
|
||||||
|
.findFirst();
|
||||||
|
verifyTRJournalEntries(optTx.get().getId(), debit(deferredIncomeLiabilityAccount, 0.33),
|
||||||
|
credit(classificationIncomeAccountRef.get(), 0.27), // First Capitalized Income With classification
|
||||||
|
credit(feeIncomeAccount, 0.06)); // Second Capitalized Income Without classification
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user