You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2025/04/25 10:35:29 UTC
(fineract) branch develop updated (834e90dce0 -> d88f634197)
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a change to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
from 834e90dce0 FINERACT-2269: various small dev-focused improvements
new 85fac1e1b0 FINERACT-2232: Capitalized income - accounting configuration
new d88f634197 FINERACT-2232: Capitalized income - income type config
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../service/ProductToGLAccountMappingHelper.java | 2 +
...tToGLAccountMappingReadPlatformServiceImpl.java | 4 +
.../accounting/common/AccountingConstants.java | 12 ++-
.../LoanProductToGLAccountMappingHelper.java | 6 ++
...ionType.java => LoanCapitalizedIncomeType.java} | 5 +-
.../loanschedule/domain/LoanApplicationTerms.java | 21 ++++--
.../loanproduct/LoanProductConstants.java | 1 +
.../api/LoanProductsApiResourceSwagger.java | 22 ++++++
.../loanproduct/data/LoanProductData.java | 25 +++++--
.../portfolio/loanproduct/domain/LoanProduct.java | 5 +-
.../domain/LoanProductRelatedDetail.java | 12 ++-
...ToGLAccountMappingWritePlatformServiceImpl.java | 6 ++
.../loanaccount/api/LoansApiResource.java | 4 +-
.../loanaccount/api/LoansApiResourceSwagger.java | 4 +
.../loanaccount/data/LoanAccountData.java | 12 ++-
.../service/LoanScheduleAssembler.java | 3 +-
.../loanaccount/service/LoanProductAssembler.java | 6 +-
.../LoanProductRelatedDetailUpdateUtil.java | 8 ++
.../service/LoanReadPlatformServiceImpl.java | 6 +-
.../loanproduct/api/LoanProductsApiResource.java | 8 +-
.../serialization/LoanProductDataValidator.java | 40 ++++++++--
.../LoanProductReadPlatformServiceImpl.java | 9 ++-
.../db/changelog/tenant/changelog-tenant.xml | 1 +
...4_loan_product_add_capitalized_income_type.xml} | 4 +-
.../domain/DefaultScheduledDateGeneratorTest.java | 4 +-
.../integrationtests/BaseLoanIntegrationTest.java | 1 +
.../fineract/integrationtests/LoanProductTest.java | 87 ++++++++++++++++++++--
27 files changed, 268 insertions(+), 50 deletions(-)
copy fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/{LoanCapitalizedIncomeCalculationType.java => LoanCapitalizedIncomeType.java} (84%)
copy fineract-provider/src/main/resources/db/changelog/tenant/parts/{0170_days_in_year_custom_strategy.xml => 0174_loan_product_add_capitalized_income_type.xml} (90%)
(fineract) 01/02: FINERACT-2232: Capitalized income - accounting configuration
Posted by ad...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 85fac1e1b09d6c6d6949f639b601beb62f07bd3f
Author: adam.magyari <ma...@gmail.com>
AuthorDate: Wed Apr 23 10:05:50 2025 +0200
FINERACT-2232: Capitalized income - accounting configuration
---
.../service/ProductToGLAccountMappingHelper.java | 2 +
...tToGLAccountMappingReadPlatformServiceImpl.java | 4 ++
.../accounting/common/AccountingConstants.java | 12 +++--
.../LoanProductToGLAccountMappingHelper.java | 6 +++
.../api/LoanProductsApiResourceSwagger.java | 10 +++++
...ToGLAccountMappingWritePlatformServiceImpl.java | 6 +++
.../serialization/LoanProductDataValidator.java | 25 +++++++++--
.../integrationtests/BaseLoanIntegrationTest.java | 1 +
.../fineract/integrationtests/LoanProductTest.java | 52 ++++++++++++++++++----
9 files changed, 103 insertions(+), 15 deletions(-)
diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
index 52d0a201b9..ca05950f13 100644
--- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
+++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
@@ -100,6 +100,8 @@ public class ProductToGLAccountMappingHelper {
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditInterestAccountId");
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditFeesAccountId");
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditPenaltyAccountId");
+ optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue());
+ optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue());
if (optionalProductToGLAccountMappingEntries.contains(paramName)) {
saveProductToAccountMapping(element, paramName, productId, accountTypeId, expectedAccountType, portfolioProductType);
diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
index 131365725b..4c24a19ace 100644
--- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
+++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
@@ -167,6 +167,10 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
gLAccountData);
+ } else if (glAccountForLoan.equals(AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY)) {
+ accountMappingDetails.put(LoanProductAccountingDataParams.DEFERRED_INCOME_LIABILITY.getValue(), gLAccountData);
+ } else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION)) {
+ accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CAPITALIZATION.getValue(), gLAccountData);
}
}
diff --git a/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java b/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
index ac6dbf5d95..8078af6c09 100644
--- a/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
+++ b/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
@@ -109,7 +109,9 @@ public final class AccountingConstants {
INCOME_FROM_CHARGE_OFF_PENALTY(18), //
INCOME_FROM_GOODWILL_CREDIT_INTEREST(19), //
INCOME_FROM_GOODWILL_CREDIT_FEES(20), //
- INCOME_FROM_GOODWILL_CREDIT_PENALTY(21); //
+ INCOME_FROM_GOODWILL_CREDIT_PENALTY(21), //
+ INCOME_FROM_CAPITALIZATION(22), //
+ DEFERRED_INCOME_LIABILITY(23); //
private final Integer value;
@@ -175,7 +177,9 @@ public final class AccountingConstants {
INCOME_FROM_GOODWILL_CREDIT_PENALTY("incomeFromGoodwillCreditPenaltyAccountId"), //
CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS("chargeOffReasonToExpenseAccountMappings"), //
EXPENSE_GL_ACCOUNT_ID("expenseAccountId"), //
- CHARGE_OFF_REASON_CODE_VALUE_ID("chargeOffReasonCodeValueId"); //
+ CHARGE_OFF_REASON_CODE_VALUE_ID("chargeOffReasonCodeValueId"), //
+ DEFERRED_INCOME_LIABILITY("deferredIncomeLiabilityAccountId"), //
+ INCOME_FROM_CAPITALIZATION("incomeFromCapitalizationAccountId"); //
private final String value;
@@ -217,7 +221,9 @@ public final class AccountingConstants {
INCOME_FROM_CHARGE_OFF_PENALTY("incomeFromChargeOffPenaltyAccount"), //
INCOME_FROM_GOODWILL_CREDIT_INTEREST("incomeFromGoodwillCreditInterestAccount"), //
INCOME_FROM_GOODWILL_CREDIT_FEES("incomeFromGoodwillCreditFeesAccount"), //
- INCOME_FROM_GOODWILL_CREDIT_PENALTY("incomeFromGoodwillCreditPenaltyAccount"); //
+ INCOME_FROM_GOODWILL_CREDIT_PENALTY("incomeFromGoodwillCreditPenaltyAccount"), //
+ DEFERRED_INCOME_LIABILITY("deferredIncomeLiabilityAccount"), //
+ INCOME_FROM_CAPITALIZATION("incomeFromCapitalizationAccount"); //
private final String value;
diff --git a/fineract-loan/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/LoanProductToGLAccountMappingHelper.java b/fineract-loan/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/LoanProductToGLAccountMappingHelper.java
index 4614044198..15dd017197 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/LoanProductToGLAccountMappingHelper.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/LoanProductToGLAccountMappingHelper.java
@@ -360,6 +360,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
mergeLoanToIncomeAccountMappingChanges(element, LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
loanProductId, AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.toString(), changes);
+ mergeLoanToIncomeAccountMappingChanges(element, LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue(),
+ loanProductId, AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION.getValue(),
+ AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION.toString(), changes);
// expenses
mergeLoanToExpenseAccountMappingChanges(element, LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
@@ -377,6 +380,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
// liabilities
mergeLoanToLiabilityAccountMappingChanges(element, LoanProductAccountingParams.OVERPAYMENT.getValue(), loanProductId,
CashAccountsForLoan.OVERPAYMENT.getValue(), CashAccountsForLoan.OVERPAYMENT.toString(), changes);
+ mergeLoanToLiabilityAccountMappingChanges(element, LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue(),
+ loanProductId, AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
+ AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.toString(), changes);
break;
}
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index caf9183f79..0ccc7ffeda 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -275,6 +275,10 @@ public final class LoanProductsApiResourceSwagger {
public Long incomeFromGoodwillCreditFeesAccountId;
@Schema(example = "11")
public Long incomeFromGoodwillCreditPenaltyAccountId;
+ @Schema(example = "25")
+ public Long deferredIncomeLiabilityAccountId;
+ @Schema(example = "37")
+ public Long incomeFromCapitalizationAccountId;
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
public List<PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
@@ -1227,6 +1231,8 @@ public final class LoanProductsApiResourceSwagger {
public GetGlAccountMapping chargeOffExpenseAccount;
public GetGlAccountMapping chargeOffFraudExpenseAccount;
public GetGlAccountMapping overpaymentLiabilityAccount;
+ public GetGlAccountMapping deferredIncomeLiabilityAccount;
+ public GetGlAccountMapping incomeFromCapitalizationAccount;
}
static final class GetLoanPaymentChannelToFundSourceMappings {
@@ -1661,6 +1667,10 @@ public final class LoanProductsApiResourceSwagger {
@Schema(example = "11")
public Long incomeFromChargeOffPenaltyAccountId;
+ @Schema(example = "25")
+ public Long deferredIncomeLiabilityAccountId;
+ @Schema(example = "37")
+ public Long incomeFromCapitalizationAccountId;
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
public List<LoanProductChargeToGLAccountMapper> feeToIncomeAccountMappings;
public List<PostLoanProductsRequest.PostChargeOffReasonToExpenseAccountMappings> chargeOffReasonToExpenseAccountMappings;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
index 9bd1011074..952c1dda25 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/productaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
@@ -187,6 +187,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), loanProductId,
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue());
+ this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+ LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue(), loanProductId,
+ AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION.getValue());
// expenses
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
@@ -205,6 +208,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
// liabilities
this.loanProductToGLAccountMappingHelper.saveLoanToLiabilityAccountMapping(element,
LoanProductAccountingParams.OVERPAYMENT.getValue(), loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue());
+ this.loanProductToGLAccountMappingHelper.saveLoanToLiabilityAccountMapping(element,
+ LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue(), loanProductId,
+ AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue());
// advanced accounting mappings
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index 425f32d012..ae62944b97 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -148,7 +148,9 @@ public final class LoanProductDataValidator {
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(),
LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue(),
- LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue(), LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME,
+ LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue(),
+ LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue(),
+ LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue(), LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME,
LoanProductConstants.PRINCIPAL_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
LoanProductConstants.INTEREST_RATE_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
LoanProductConstants.NUMBER_OF_REPAYMENT_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME, LoanProductConstants.SHORT_NAME,
@@ -882,7 +884,7 @@ public final class LoanProductDataValidator {
"Charge off behaviour is only supported for Progressive loans");
}
- validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
+ validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator, accountingRuleType);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -1930,7 +1932,7 @@ public final class LoanProductDataValidator {
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
- validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
+ validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator, accountingRuleType);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -2671,7 +2673,7 @@ public final class LoanProductDataValidator {
}
private void validateIncomeCapitalization(String transactionProcessingStrategyCode, JsonElement element,
- DataValidatorBuilder baseDataValidator) {
+ DataValidatorBuilder baseDataValidator, Integer accountingRuleType) {
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, element)) {
final String capitalizedIncomeCalculationType = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, element);
@@ -2703,6 +2705,21 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME)
.value(capitalizedIncomeStrategy).isOneOfEnumValues(LoanCapitalizedIncomeStrategy.class)
.cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, true);
+ // Accounting
+ if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
+ final Long deferredIncomeLiabilityAccountId = this.fromApiJsonHelper
+ .extractLongNamed(LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue(), element);
+ baseDataValidator.reset().parameter(LoanProductAccountingParams.DEFERRED_INCOME_LIABILITY.getValue())
+ .value(deferredIncomeLiabilityAccountId).notNull()
+ .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, true)
+ .integerGreaterThanZero();
+ final Long incomeFromDeferredIncomeAccountId = this.fromApiJsonHelper
+ .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue(), element);
+ baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_CAPITALIZATION.getValue())
+ .value(incomeFromDeferredIncomeAccountId).notNull()
+ .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, true)
+ .integerGreaterThanZero();
+ }
}
} else if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element)) {
Boolean enableIncomeCapitalization = this.fromApiJsonHelper
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index ee4e6681d3..c696a1fbbc 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -158,6 +158,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
protected final Account writtenOffAccount = accountHelper.createExpenseAccount("writtenOffAccount");
protected final Account goodwillExpenseAccount = accountHelper.createExpenseAccount("goodwillExpenseAccount");
protected final Account goodwillIncomeAccount = accountHelper.createIncomeAccount("goodwillIncomeAccount");
+ protected final Account deferredIncomeLiabilityAccount = accountHelper.createLiabilityAccount("deferredIncomeLiabilityAccount");
protected final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
protected ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
index 747485d59c..0a6e22cf9d 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
@@ -44,7 +44,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
- .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -80,7 +82,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(false)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
- .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -114,7 +118,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
- .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -125,9 +131,12 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(loanProductsProductIdResponse.getAccountingMappings());
+ Assertions.assertEquals(feeIncomeAccount.getAccountID().longValue(),
+ loanProductsProductIdResponse.getAccountingMappings().getIncomeFromCapitalizationAccount().getId());
- loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(),
- new PutLoanProductsProductIdRequest().enableIncomeCapitalization(false));
+ loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(), new PutLoanProductsProductIdRequest()
+ .enableIncomeCapitalization(false).incomeFromCapitalizationAccountId(interestIncomeAccount.getAccountID().longValue()));
final GetLoanProductsProductIdResponse updatedLoanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -138,6 +147,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
updatedLoanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getAccountingMappings());
+ Assertions.assertEquals(interestIncomeAccount.getAccountID().longValue(),
+ updatedLoanProductsProductIdResponse.getAccountingMappings().getIncomeFromCapitalizationAccount().getId());
}
@Test
@@ -147,21 +159,45 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
() -> loanProductHelper.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(7.0)
.enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
- .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)));
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
}
@Test
public void testIncomeCapitalizationEnabledCalculationTypeNotProvided() {
Assertions.assertThrows(RuntimeException.class,
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
- .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)));
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
}
@Test
public void testIncomeCapitalizationEnabledStrategyNotProvided() {
Assertions.assertThrows(RuntimeException.class,
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
- .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)));
+ .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ }
+
+ @Test
+ public void testIncomeCapitalizationEnabledDeferredIncomeLiabilityNotProvided() {
+ Assertions.assertThrows(RuntimeException.class,
+ () -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
+ .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ }
+
+ @Test
+ public void testIncomeCapitalizationEnabledIncomeFromCapitalizationNotProvided() {
+ Assertions.assertThrows(RuntimeException.class,
+ () -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
+ .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())));
}
}
}
(fineract) 02/02: FINERACT-2232: Capitalized income - income type config
Posted by ad...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit d88f634197688472530a4fa58a92b7fdbce912b6
Author: adam.magyari <ma...@gmail.com>
AuthorDate: Thu Apr 24 07:56:57 2025 +0200
FINERACT-2232: Capitalized income - income type config
---
.../domain/LoanCapitalizedIncomeType.java | 33 +++++++++++++
.../loanschedule/domain/LoanApplicationTerms.java | 21 ++++++--
.../loanproduct/LoanProductConstants.java | 1 +
.../api/LoanProductsApiResourceSwagger.java | 12 +++++
.../loanproduct/data/LoanProductData.java | 25 +++++++---
.../portfolio/loanproduct/domain/LoanProduct.java | 5 +-
.../domain/LoanProductRelatedDetail.java | 12 +++--
.../loanaccount/api/LoansApiResource.java | 4 +-
.../loanaccount/api/LoansApiResourceSwagger.java | 4 ++
.../loanaccount/data/LoanAccountData.java | 12 +++--
.../service/LoanScheduleAssembler.java | 3 +-
.../loanaccount/service/LoanProductAssembler.java | 6 ++-
.../LoanProductRelatedDetailUpdateUtil.java | 8 +++
.../service/LoanReadPlatformServiceImpl.java | 6 ++-
.../loanproduct/api/LoanProductsApiResource.java | 8 ++-
.../serialization/LoanProductDataValidator.java | 15 +++++-
.../LoanProductReadPlatformServiceImpl.java | 9 +++-
.../db/changelog/tenant/changelog-tenant.xml | 1 +
...74_loan_product_add_capitalized_income_type.xml | 37 ++++++++++++++
.../domain/DefaultScheduledDateGeneratorTest.java | 4 +-
.../fineract/integrationtests/LoanProductTest.java | 57 ++++++++++++++++++----
21 files changed, 242 insertions(+), 41 deletions(-)
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCapitalizedIncomeType.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCapitalizedIncomeType.java
new file mode 100644
index 0000000000..58d233135e
--- /dev/null
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCapitalizedIncomeType.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.domain;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.api.ApiFacingEnum;
+
+@Getter
+@RequiredArgsConstructor
+public enum LoanCapitalizedIncomeType implements ApiFacingEnum<LoanCapitalizedIncomeType> {
+
+ FEE("capitalizedIncome.incomeType.fee", "Fee"), INTEREST("capitalizedIncome.incomeType.interest", "Interest");
+
+ private final String code;
+ private final String humanReadableName;
+}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index f517a1b240..2c2cad5bbc 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
@@ -237,6 +238,7 @@ public final class LoanApplicationTerms {
private boolean enableIncomeCapitalization;
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
+ private LoanCapitalizedIncomeType capitalizedIncomeType;
private LoanApplicationTerms(Builder builder) {
this.currency = builder.currency;
@@ -276,6 +278,7 @@ public final class LoanApplicationTerms {
this.enableIncomeCapitalization = builder.enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = builder.capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = builder.capitalizedIncomeStrategy;
+ this.capitalizedIncomeType = builder.capitalizedIncomeType;
}
public static class Builder {
@@ -308,6 +311,7 @@ public final class LoanApplicationTerms {
private boolean enableIncomeCapitalization;
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
+ private LoanCapitalizedIncomeType capitalizedIncomeType;
public Builder currency(CurrencyData currency) {
this.currency = currency;
@@ -439,6 +443,11 @@ public final class LoanApplicationTerms {
return this;
}
+ public Builder capitalizedIncomeType(LoanCapitalizedIncomeType value) {
+ this.capitalizedIncomeType = value;
+ return this;
+ }
+
public LoanApplicationTerms build() {
return new LoanApplicationTerms(this);
}
@@ -508,7 +517,7 @@ public final class LoanApplicationTerms {
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
- final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
+ final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) {
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
@@ -529,7 +538,7 @@ public final class LoanApplicationTerms {
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
@@ -605,7 +614,7 @@ public final class LoanApplicationTerms {
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(),
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(), loanProductRelatedDetail.getDaysInYearCustomStrategy(),
loanProductRelatedDetail.isEnableIncomeCapitalization(), loanProductRelatedDetail.getCapitalizedIncomeCalculationType(),
- loanProductRelatedDetail.getCapitalizedIncomeStrategy());
+ loanProductRelatedDetail.getCapitalizedIncomeStrategy(), loanProductRelatedDetail.getCapitalizedIncomeType());
}
private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency,
@@ -638,7 +647,7 @@ public final class LoanApplicationTerms {
final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour,
final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy,
final boolean enableIncomeCapitalization, final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
- final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
+ final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) {
this.currency = currency;
this.loanTermFrequency = loanTermFrequency;
@@ -743,6 +752,7 @@ public final class LoanApplicationTerms {
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
+ this.capitalizedIncomeType = capitalizedIncomeType;
}
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -1605,7 +1615,8 @@ public final class LoanApplicationTerms {
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy,
- this.enableIncomeCapitalization, this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy);
+ this.enableIncomeCapitalization, this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy,
+ this.capitalizedIncomeType);
}
public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
index 1e803ab0af..bda1839495 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
@@ -176,4 +176,5 @@ public interface LoanProductConstants {
String ENABLE_INCOME_CAPITALIZATION_PARAM_NAME = "enableIncomeCapitalization";
String CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME = "capitalizedIncomeCalculationType";
String CAPITALIZED_INCOME_STRATEGY_PARAM_NAME = "capitalizedIncomeStrategy";
+ String CAPITALIZED_INCOME_TYPE_PARAM_NAME = "capitalizedIncomeType";
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 0ccc7ffeda..3e12aca89f 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -201,6 +201,8 @@ public final class LoanProductsApiResourceSwagger {
public String capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
public String capitalizedIncomeStrategy;
+ @Schema(example = "FEE", allowableValues = { "FEE", "INTEREST" })
+ public String capitalizedIncomeType;
// Interest Recalculation
@Schema(example = "false")
@@ -667,6 +669,8 @@ public final class LoanProductsApiResourceSwagger {
public StringEnumOptionData capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION")
public StringEnumOptionData capitalizedIncomeStrategy;
+ @Schema(example = "FEE")
+ public StringEnumOptionData capitalizedIncomeType;
}
@Schema(description = "GetLoanProductsTemplateResponse")
@@ -1119,8 +1123,11 @@ public final class LoanProductsApiResourceSwagger {
public StringEnumOptionData capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION")
public StringEnumOptionData capitalizedIncomeStrategy;
+ @Schema(example = "FEE")
+ public StringEnumOptionData capitalizedIncomeType;
public List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
public List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
+ public List<StringEnumOptionData> capitalizedIncomeTypeOptions;
}
@Schema(description = "GetLoanProductsProductIdResponse")
@@ -1448,8 +1455,11 @@ public final class LoanProductsApiResourceSwagger {
public StringEnumOptionData capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION")
public StringEnumOptionData capitalizedIncomeStrategy;
+ @Schema(example = "FEE")
+ public StringEnumOptionData capitalizedIncomeType;
public List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
public List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
+ public List<StringEnumOptionData> capitalizedIncomeTypeOptions;
}
@Schema(description = "PutLoanProductsProductIdRequest")
@@ -1713,6 +1723,8 @@ public final class LoanProductsApiResourceSwagger {
public String capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
public String capitalizedIncomeStrategy;
+ @Schema(example = "FEE", allowableValues = { "FEE", "INTEREST" })
+ public String capitalizedIncomeType;
}
public static final class AdvancedPaymentData {
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index bcece9c7a4..a9e0bfbcc5 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -51,6 +51,7 @@ import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -244,8 +245,10 @@ public class LoanProductData implements Serializable {
private Boolean enableIncomeCapitalization;
private StringEnumOptionData capitalizedIncomeCalculationType;
private StringEnumOptionData capitalizedIncomeStrategy;
+ private final StringEnumOptionData capitalizedIncomeType;
private List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
private List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
+ private List<StringEnumOptionData> capitalizedIncomeTypeOptions;
/**
* Used when returning lookup information about loan product for dropdowns.
@@ -353,6 +356,7 @@ public class LoanProductData implements Serializable {
final boolean enableIncomeCapitalization = false;
final StringEnumOptionData capitalizedIncomeCalculationType = null;
final StringEnumOptionData capitalizedIncomeStrategy = null;
+ final StringEnumOptionData capitalizedIncomeType = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -375,7 +379,7 @@ public class LoanProductData implements Serializable {
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
@@ -483,6 +487,7 @@ public class LoanProductData implements Serializable {
final boolean enableIncomeCapitalization = false;
final StringEnumOptionData capitalizedIncomeCalculationType = null;
final StringEnumOptionData capitalizedIncomeStrategy = null;
+ final StringEnumOptionData capitalizedIncomeType = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -505,7 +510,7 @@ public class LoanProductData implements Serializable {
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
@@ -620,6 +625,7 @@ public class LoanProductData implements Serializable {
final boolean enableIncomeCapitalization = false;
final StringEnumOptionData capitalizedIncomeCalculationType = null;
final StringEnumOptionData capitalizedIncomeStrategy = null;
+ final StringEnumOptionData capitalizedIncomeType = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -642,7 +648,7 @@ public class LoanProductData implements Serializable {
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
@@ -751,6 +757,7 @@ public class LoanProductData implements Serializable {
final boolean enableIncomeCapitalization = false;
final StringEnumOptionData capitalizedIncomeCalculationType = null;
final StringEnumOptionData capitalizedIncomeStrategy = null;
+ final StringEnumOptionData capitalizedIncomeType = null;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -773,7 +780,7 @@ public class LoanProductData implements Serializable {
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
@@ -829,7 +836,8 @@ public class LoanProductData implements Serializable {
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
final StringEnumOptionData daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
- final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy) {
+ final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy,
+ final StringEnumOptionData capitalizedIncomeType) {
this.id = id;
this.name = name;
this.shortName = shortName;
@@ -889,6 +897,7 @@ public class LoanProductData implements Serializable {
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
+ this.capitalizedIncomeType = capitalizedIncomeType;
this.chargeOptions = null;
this.penaltyOptions = null;
@@ -981,6 +990,7 @@ public class LoanProductData implements Serializable {
this.capitalizedIncomeCalculationTypeOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeCalculationType.class);
this.capitalizedIncomeStrategyOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class);
+ this.capitalizedIncomeTypeOptions = ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class);
}
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -1007,7 +1017,8 @@ public class LoanProductData implements Serializable {
final List<StringEnumOptionData> chargeOffBehaviourOptions, final List<CodeValueData> chargeOffReasonOptions,
final List<StringEnumOptionData> daysInYearCustomStrategyOptions,
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions,
- final List<StringEnumOptionData> capitalizedIncomeStrategyOptions) {
+ final List<StringEnumOptionData> capitalizedIncomeStrategyOptions,
+ final List<StringEnumOptionData> capitalizedIncomeTypeOptions) {
this.id = productData.id;
this.name = productData.name;
@@ -1174,8 +1185,10 @@ public class LoanProductData implements Serializable {
this.enableIncomeCapitalization = productData.enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = productData.capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = productData.capitalizedIncomeStrategy;
+ this.capitalizedIncomeType = productData.capitalizedIncomeType;
this.capitalizedIncomeCalculationTypeOptions = capitalizedIncomeCalculationTypeOptions;
this.capitalizedIncomeStrategyOptions = capitalizedIncomeStrategyOptions;
+ this.capitalizedIncomeTypeOptions = capitalizedIncomeTypeOptions;
}
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index 31e4ab8a1d..d9f7dbd713 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -61,6 +61,7 @@ import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -284,7 +285,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate,
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
- final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
+ final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) {
this.fund = fund;
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
@@ -335,7 +336,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy,
- enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index 29b18e6cea..6ca788655e 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -181,6 +182,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "capitalized_income_strategy")
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
+ @Enumerated(EnumType.STRING)
+ @Column(name = "capitalized_income_type")
+ private LoanCapitalizedIncomeType capitalizedIncomeType;
+
public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal,
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
@@ -197,7 +202,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
- final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
+ final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) {
final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData);
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
@@ -208,7 +213,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
protected LoanProductRelatedDetail() {
@@ -231,7 +236,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
- final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
+ final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType) {
this.currency = currency;
this.principal = defaultPrincipal;
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
@@ -272,6 +277,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
+ this.capitalizedIncomeType = capitalizedIncomeType;
}
private Integer defaultToNullIfZero(final Integer value) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index 04dde09757..2951426646 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -135,6 +135,7 @@ import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
@@ -1160,7 +1161,8 @@ public class LoansApiResource {
LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList(),
loanTermVariations, ApiFacingEnum.getValuesAsStringEnumOptionDataList(DaysInYearCustomStrategyType.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeCalculationType.class),
- ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class));
+ ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class),
+ ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 17787426d8..0b146908aa 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -1212,6 +1212,8 @@ final class LoansApiResourceSwagger {
public StringEnumOptionData capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION")
public StringEnumOptionData capitalizedIncomeStrategy;
+ @Schema(example = "FEE")
+ public StringEnumOptionData capitalizedIncomeType;
}
@Schema(description = "GetLoansResponse")
@@ -1321,6 +1323,8 @@ final class LoansApiResourceSwagger {
public String capitalizedIncomeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
public String capitalizedIncomeStrategy;
+ @Schema(example = "FEE")
+ public StringEnumOptionData capitalizedIncomeType;
public List<PostLoansRequestChargeData> charges;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
index 4376cf077c..f2e4b2f355 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
@@ -174,6 +174,7 @@ public class LoanAccountData {
private List<StringEnumOptionData> daysInYearCustomStrategyOptions;
private List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
private List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
+ private List<StringEnumOptionData> capitalizedIncomeTypeOptions;
@Transient
private BigDecimal feeChargesAtDisbursementCharged;
@@ -279,6 +280,7 @@ public class LoanAccountData {
private Boolean enableIncomeCapitalization;
private StringEnumOptionData capitalizedIncomeCalculationType;
private StringEnumOptionData capitalizedIncomeStrategy;
+ private StringEnumOptionData capitalizedIncomeType;
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
@@ -472,7 +474,7 @@ public class LoanAccountData {
final EnumOptionData loanScheduleProcessingType, final Integer fixedLength, final StringEnumOptionData chargeOffBehaviour,
final boolean isInterestRecognitionOnDisbursementDate, final StringEnumOptionData daysInYearCustomStrategy,
final boolean enableIncomeCapitalization, final StringEnumOptionData capitalizedIncomeCalculationType,
- final StringEnumOptionData capitalizedIncomeStrategy) {
+ final StringEnumOptionData capitalizedIncomeStrategy, StringEnumOptionData capitalizedIncomeType) {
final CollectionData delinquent = CollectionData.template();
@@ -520,7 +522,7 @@ public class LoanAccountData {
.setChargeOffBehaviour(chargeOffBehaviour).setInterestRecognitionOnDisbursementDate(isInterestRecognitionOnDisbursementDate)
.setDaysInYearCustomStrategy(daysInYearCustomStrategy).setEnableIncomeCapitalization(enableIncomeCapitalization)
.setCapitalizedIncomeCalculationType(capitalizedIncomeCalculationType)
- .setCapitalizedIncomeStrategy(capitalizedIncomeStrategy);
+ .setCapitalizedIncomeStrategy(capitalizedIncomeStrategy).setCapitalizedIncomeType(capitalizedIncomeType);
}
/*
@@ -548,7 +550,8 @@ public class LoanAccountData {
final List<EnumOptionData> loanScheduleProcessingTypeOptions, final List<LoanTermVariationsData> loanTermVariations,
final List<StringEnumOptionData> daysInYearCustomStrategyOptions,
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions,
- final List<StringEnumOptionData> capitalizedIncomeStrategyOptions) {
+ final List<StringEnumOptionData> capitalizedIncomeStrategyOptions,
+ final List<StringEnumOptionData> capitalizedIncomeTypeOptions) {
// TODO: why are these variables 'calendarData', 'chargeTemplate' never used (see original private constructor)
@@ -571,7 +574,8 @@ public class LoanAccountData {
.setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions).setLoanTermVariations(loanTermVariations)
.setDaysInYearCustomStrategyOptions(daysInYearCustomStrategyOptions)
.setCapitalizedIncomeCalculationTypeOptions(capitalizedIncomeCalculationTypeOptions)
- .setCapitalizedIncomeStrategyOptions(capitalizedIncomeStrategyOptions);
+ .setCapitalizedIncomeStrategyOptions(capitalizedIncomeStrategyOptions)
+ .setCapitalizedIncomeTypeOptions(capitalizedIncomeTypeOptions);
}
public LoanAccountData associationsAndTemplate(final Collection<LoanProductData> productOptions,
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index fe27ecb806..2db86adb9b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -547,7 +547,8 @@ public class LoanScheduleAssembler {
loanProduct.getLoanProductRelatedDetail().getDaysInYearCustomStrategy(),
loanProduct.getLoanProductRelatedDetail().isEnableIncomeCapitalization(),
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeCalculationType(),
- loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy());
+ loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(),
+ loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeType());
}
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
index 85522bed1a..2e2a3beb6b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
@@ -42,6 +42,7 @@ import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
@@ -313,6 +314,8 @@ public class LoanProductAssembler {
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, LoanCapitalizedIncomeCalculationType.class);
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy = command.enumValueOfParameterNamed(
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanCapitalizedIncomeStrategy.class);
+ final LoanCapitalizedIncomeType capitalizedIncomeType = command
+ .enumValueOfParameterNamed(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, LoanCapitalizedIncomeType.class);
return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules,
name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
@@ -334,7 +337,8 @@ public class LoanProductAssembler {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate,
- daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+ capitalizedIncomeType);
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
index da78ccf984..b84cc31e09 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
@@ -28,6 +28,7 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -320,6 +321,13 @@ public class LoanProductRelatedDetailUpdateUtil {
loanRepaymentScheduleDetail.setCapitalizedIncomeStrategy(newValue);
}
+ if (command.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME)) {
+ final LoanCapitalizedIncomeType newValue = command
+ .enumValueOfParameterNamed(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, LoanCapitalizedIncomeType.class);
+ actualChanges.put(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, newValue);
+ loanRepaymentScheduleDetail.setCapitalizedIncomeType(newValue);
+ }
+
return actualChanges;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index f853b38d57..1ec838ec76 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -109,6 +109,7 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
@@ -737,6 +738,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
+ " l.enable_income_capitalization as enableIncomeCapitalization, "
+ " l.capitalized_income_calculation_type as capitalizedIncomeCalculationType, "
+ " l.capitalized_income_strategy as capitalizedIncomeStrategy, "
+ + " l.capitalized_income_type as capitalizedIncomeType, " //
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, "
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
@@ -1118,6 +1120,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
.getStringEnumOptionData(LoanCapitalizedIncomeCalculationType.class, rs.getString("capitalizedIncomeCalculationType"));
final StringEnumOptionData capitalizedIncomeStrategy = ApiFacingEnum
.getStringEnumOptionData(LoanCapitalizedIncomeStrategy.class, rs.getString("capitalizedIncomeStrategy"));
+ final StringEnumOptionData capitalizedIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanCapitalizedIncomeType.class,
+ rs.getString("capitalizedIncomeType"));
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
@@ -1138,7 +1142,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
- capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index eb41c4228d..5604d7ff51 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -84,6 +84,7 @@ import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -133,7 +134,7 @@ public class LoanProductsApiResource {
LoanProductConstants.REPAYMENT_START_DATE_TYPE, LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME,
LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME,
- LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME));
+ LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME));
private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
@@ -450,6 +451,8 @@ public class LoanProductsApiResource {
.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeCalculationType.class);
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class);
+ final List<StringEnumOptionData> capitalizedIncomeTypeOptions = ApiFacingEnum
+ .getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class);
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
@@ -462,7 +465,8 @@ public class LoanProductsApiResource {
advancedPaymentAllocationTypes, LoanScheduleType.getValuesAsEnumOptionDataList(),
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
- daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions);
+ daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions,
+ capitalizedIncomeTypeOptions);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index ae62944b97..3a298ce89d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -51,6 +51,7 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
@@ -193,7 +194,7 @@ public final class LoanProductDataValidator {
LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME,
LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME,
- LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME));
+ LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME));
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
@@ -2688,6 +2689,13 @@ public final class LoanProductDataValidator {
.value(capitalizedIncomeStrategy).isOneOfEnumValues(LoanCapitalizedIncomeStrategy.class);
}
+ if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, element)) {
+ final String capitalizedIncomeType = this.fromApiJsonHelper
+ .extractStringNamed(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, element);
+ baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME).value(capitalizedIncomeType)
+ .isOneOfEnumValues(LoanCapitalizedIncomeType.class);
+ }
+
if (AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equals(transactionProcessingStrategyCode)
&& this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element)) {
Boolean enableIncomeCapitalization = this.fromApiJsonHelper
@@ -2705,6 +2713,11 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME)
.value(capitalizedIncomeStrategy).isOneOfEnumValues(LoanCapitalizedIncomeStrategy.class)
.cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, true);
+ final String capitalizedIncomeType = this.fromApiJsonHelper
+ .extractStringNamed(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, element);
+ baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME).value(capitalizedIncomeType)
+ .isOneOfEnumValues(LoanCapitalizedIncomeType.class)
+ .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, true);
// Accounting
if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
final Long deferredIncomeLiabilityAccountId = this.fromApiJsonHelper
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 7c316aa180..91aad12aba 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -294,7 +295,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.charge_off_behaviour as chargeOffBehaviour, " //
+ "lp.enable_income_capitalization as enableIncomeCapitalization, " //
+ "lp.capitalized_income_calculation_type as capitalizedIncomeCalculationType, " //
- + "lp.capitalized_income_strategy as capitalizedIncomeStrategy " //
+ + "lp.capitalized_income_strategy as capitalizedIncomeStrategy, " //
+ + "lp.capitalized_income_type as capitalizedIncomeType " //
+ " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id "
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
+ " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id "
@@ -563,6 +565,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
.getStringEnumOptionData(LoanCapitalizedIncomeCalculationType.class, rs.getString("capitalizedIncomeCalculationType"));
final StringEnumOptionData capitalizedIncomeStrategy = ApiFacingEnum
.getStringEnumOptionData(LoanCapitalizedIncomeStrategy.class, rs.getString("capitalizedIncomeStrategy"));
+ final StringEnumOptionData capitalizedIncome = ApiFacingEnum.getStringEnumOptionData(LoanCapitalizedIncomeType.class,
+ rs.getString("capitalizedIncomeType"));
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -587,7 +591,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate,
- daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
+ daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+ capitalizedIncome);
}
}
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index e677629f12..6e48c15a4f 100644
--- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -192,4 +192,5 @@
<include file="parts/0171_loan_capitalized_income.xml" relativeToChangelogFile="true"/>
<include file="parts/0172_create_loan_capitalized_income_balance.xml" relativeToChangelogFile="true"/>
<include file="parts/0173_user_change_pwd.xml" relativeToChangelogFile="true" />
+ <include file="parts/0174_loan_product_add_capitalized_income_type.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0174_loan_product_add_capitalized_income_type.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0174_loan_product_add_capitalized_income_type.xml
new file mode 100644
index 0000000000..e3e3d20427
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0174_loan_product_add_capitalized_income_type.xml
@@ -0,0 +1,37 @@
+<?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 author="fineract" id="1">
+ <addColumn tableName="m_loan">
+ <column name="capitalized_income_type" type="VARCHAR(10)">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+ <addColumn tableName="m_product_loan">
+ <column name="capitalized_income_type" type="VARCHAR(10)">
+ <constraints nullable="true"/>
+ </column>
+ </addColumn>
+ </changeSet>
+</databaseChangeLog>
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
index d0d83caa6c..43052d0f80 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGeneratorTest.java
@@ -98,7 +98,7 @@ public class DefaultScheduledDateGeneratorTest {
Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null,
DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO,
EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, DISBURSEMENT_DATE,
- submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false, null, false, null,
+ submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false, null, false, null, null,
null);
// when
@@ -180,7 +180,7 @@ public class DefaultScheduledDateGeneratorTest {
EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null,
null, ZERO, null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false,
null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null,
- false, null, false, null, null);
+ false, null, false, null, null, null);
}
private HolidayDetailDTO createHolidayDTO() {
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
index 0a6e22cf9d..6d2b2f33b4 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductTest.java
@@ -28,6 +28,7 @@ import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -46,7 +47,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -57,6 +59,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.FEE.getCode(),
+ loanProductsProductIdResponse.getCapitalizedIncomeType().getCode());
runAt("20 December 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
@@ -70,6 +75,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
loanDetails.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(loanDetails.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.FEE.getCode(), loanDetails.getCapitalizedIncomeType().getCode());
Assertions.assertDoesNotThrow(() -> disburseLoan(loanId, BigDecimal.valueOf(430), "20 December 2024"));
});
@@ -84,7 +91,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -95,6 +103,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.FEE.getCode(),
+ loanProductsProductIdResponse.getCapitalizedIncomeType().getCode());
runAt("20 December 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
@@ -108,6 +119,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeStrategy());
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
loanDetails.getCapitalizedIncomeStrategy().getCode());
+ Assertions.assertNotNull(loanDetails.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.FEE.getCode(), loanDetails.getCapitalizedIncomeType().getCode());
Assertions.assertDoesNotThrow(() -> disburseLoan(loanId, BigDecimal.valueOf(430), "20 December 2024"));
});
@@ -120,7 +133,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -134,9 +148,14 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(loanProductsProductIdResponse.getAccountingMappings());
Assertions.assertEquals(feeIncomeAccount.getAccountID().longValue(),
loanProductsProductIdResponse.getAccountingMappings().getIncomeFromCapitalizationAccount().getId());
+ Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.FEE.getCode(),
+ loanProductsProductIdResponse.getCapitalizedIncomeType().getCode());
- loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(), new PutLoanProductsProductIdRequest()
- .enableIncomeCapitalization(false).incomeFromCapitalizationAccountId(interestIncomeAccount.getAccountID().longValue()));
+ loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(),
+ new PutLoanProductsProductIdRequest().enableIncomeCapitalization(false)
+ .incomeFromCapitalizationAccountId(interestIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PutLoanProductsProductIdRequest.CapitalizedIncomeTypeEnum.INTEREST));
final GetLoanProductsProductIdResponse updatedLoanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductsResponse.getResourceId());
@@ -150,6 +169,9 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getAccountingMappings());
Assertions.assertEquals(interestIncomeAccount.getAccountID().longValue(),
updatedLoanProductsProductIdResponse.getAccountingMappings().getIncomeFromCapitalizationAccount().getId());
+ Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getCapitalizedIncomeType());
+ Assertions.assertEquals(LoanCapitalizedIncomeType.INTEREST.getCode(),
+ updatedLoanProductsProductIdResponse.getCapitalizedIncomeType().getCode());
}
@Test
@@ -161,7 +183,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)));
}
@Test
@@ -170,7 +193,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)));
}
@Test
@@ -179,7 +203,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)));
}
@Test
@@ -188,7 +213,8 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
- .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)));
}
@Test
@@ -197,7 +223,18 @@ public class LoanProductTest extends BaseLoanIntegrationTest {
() -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
- .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())));
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)));
+ }
+
+ @Test
+ public void testIncomeCapitalizationEnabledIncomeTypeNotProvided() {
+ Assertions.assertThrows(RuntimeException.class,
+ () -> loanProductHelper.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
+ .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
+ .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
+ .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
+ .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())));
}
}
}