From 6bbc4bd3f39162af01611567ba948423844e7f4b Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Fri, 8 Aug 2025 08:08:05 -0500 Subject: [PATCH] FINERACT-2330: Buy-Down fees Accounting for non merchant product --- ...oGLAccountMappingWritePlatformService.java | 3 +- .../factory/LoanProductsRequestFactory.java | 1 + .../LoanProductToGLAccountMappingHelper.java | 10 +++-- .../data/AccountingBridgeDataDTO.java | 1 + .../loanaccount/data/LoanAccountData.java | 7 ++- .../domain/LoanApplicationTerms.java | 22 +++++++--- .../mapper/LoanAccountingBridgeMapper.java | 6 +-- .../loanproduct/LoanProductConstants.java | 1 + .../api/LoanProductsApiResourceSwagger.java | 10 +++++ .../loanproduct/data/LoanProductData.java | 18 +++++--- .../loanproduct/domain/LoanProduct.java | 6 ++- .../domain/LoanProductRelatedDetail.java | 10 +++-- .../module/loan/module-changelog-master.xml | 1 + .../parts/1031_loan_merchant_buy_down_fee.xml | 37 ++++++++++++++++ .../accounting/journalentry/data/LoanDTO.java | 1 + .../service/AccountingProcessorHelper.java | 3 +- ...ccrualBasedAccountingProcessorForLoan.java | 4 +- ...ccountMappingWritePlatformServiceImpl.java | 21 ++++++--- .../service/LoanScheduleAssembler.java | 3 +- .../LoanAccrualsProcessingServiceImpl.java | 2 +- .../service/LoanProductAssembler.java | 5 ++- .../LoanProductRelatedDetailUpdateUtil.java | 7 +++ .../service/LoanReadPlatformServiceImpl.java | 9 ++-- .../LoanProductDataValidator.java | 22 +++++++--- .../LoanProductReadPlatformServiceImpl.java | 6 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 3 +- ...ateJournalEntriesForChargeOffLoanTest.java | 3 +- .../DefaultScheduledDateGeneratorTest.java | 4 +- .../integrationtests/LoanBuyDownFeeTest.java | 44 ++++++++++++++++++- .../integrationtests/LoanProductTest.java | 16 +++---- .../integrationtests/LoanTransactionTest.java | 2 +- .../common/loans/LoanProductTestBuilder.java | 10 ++++- 32 files changed, 233 insertions(+), 65 deletions(-) create mode 100644 fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1031_loan_merchant_buy_down_fee.xml diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java index 2495537df..90ade7b0f 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformService.java @@ -30,7 +30,8 @@ public interface ProductToGLAccountMappingWritePlatformService { void createSavingProductToGLAccountMapping(Long savingProductId, JsonCommand command, DepositAccountType accountType); Map updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged, - AccountingRuleType accountingRuleTypeId, boolean enableIncomeCapitalization, boolean enableBuyDownFee); + AccountingRuleType accountingRuleTypeId, boolean enableIncomeCapitalization, boolean enableBuyDownFee, + boolean merchantBuyDownFee); Map updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged, int accountingRuleTypeId, DepositAccountType accountType); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java index 3ef70564c..ab9d08312 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java @@ -1751,6 +1751,7 @@ public class LoanProductsRequestFactory { .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT)// .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.INTEREST)// .enableBuyDownFee(true)// + .merchantBuyDownFee(true)// .buyDownExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.BUY_DOWN_EXPENSE))// .incomeFromBuyDownAccountId(accountTypeResolver.resolve(DefaultAccountType.INCOME_FROM_BUY_DOWN));// } 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 36f80f9a4..44439a316 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 @@ -256,7 +256,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi */ public void handleChangesToLoanProductToGLAccountMappings(final Long loanProductId, final Map changes, final JsonElement element, final AccountingRuleType accountingRuleType, final boolean enableIncomeCapitalization, - final boolean enableBuyDownFee) { + final boolean enableBuyDownFee, final boolean merchantBuyDownFee) { switch (accountingRuleType) { case NONE: break; @@ -399,9 +399,11 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi deleteProductToGLAccountMapping(loanProductId, PortfolioProductType.LOAN, AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue()); } else { - mergeLoanToExpenseAccountMappingChanges(element, LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), loanProductId, - AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue(), AccrualAccountsForLoan.BUY_DOWN_EXPENSE.toString(), - changes); + if (merchantBuyDownFee) { + mergeLoanToExpenseAccountMappingChanges(element, LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), + loanProductId, AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue(), + AccrualAccountsForLoan.BUY_DOWN_EXPENSE.toString(), changes); + } } // liabilities diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccountingBridgeDataDTO.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccountingBridgeDataDTO.java index 784136c05..b68c9e18d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccountingBridgeDataDTO.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccountingBridgeDataDTO.java @@ -46,5 +46,6 @@ public class AccountingBridgeDataDTO { private Long chargeOffReasonCodeValue; private boolean isWrittenOff; private List newLoanTransactions = new ArrayList<>(); + private boolean merchantBuyDownFee; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index 221509905..bca5e59bd 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -288,6 +288,7 @@ public class LoanAccountData { private StringEnumOptionData buyDownFeeCalculationType; private StringEnumOptionData buyDownFeeStrategy; private StringEnumOptionData buyDownFeeIncomeType; + private Boolean merchantBuyDownFee; public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId, Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments, @@ -483,7 +484,8 @@ public class LoanAccountData { final boolean enableIncomeCapitalization, final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy, StringEnumOptionData capitalizedIncomeType, final boolean enableBuyDownFee, final StringEnumOptionData buyDownFeeCalculationType, - final StringEnumOptionData buyDownFeeStrategy, final StringEnumOptionData buyDownFeeIncomeType) { + final StringEnumOptionData buyDownFeeStrategy, final StringEnumOptionData buyDownFeeIncomeType, + final boolean merchantBuyDownFee) { final CollectionData delinquent = CollectionData.template(); @@ -533,7 +535,8 @@ public class LoanAccountData { .setCapitalizedIncomeCalculationType(capitalizedIncomeCalculationType) .setCapitalizedIncomeStrategy(capitalizedIncomeStrategy).setCapitalizedIncomeType(capitalizedIncomeType) .setEnableBuyDownFee(enableBuyDownFee).setBuyDownFeeCalculationType(buyDownFeeCalculationType) - .setBuyDownFeeStrategy(buyDownFeeStrategy).setBuyDownFeeIncomeType(buyDownFeeIncomeType); + .setBuyDownFeeStrategy(buyDownFeeStrategy).setBuyDownFeeIncomeType(buyDownFeeIncomeType) + .setMerchantBuyDownFee(merchantBuyDownFee); } /* 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 0ec0d57e8..d9c7b1058 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 @@ -246,6 +246,7 @@ public final class LoanApplicationTerms { private LoanBuyDownFeeCalculationType buyDownFeeCalculationType; private LoanBuyDownFeeStrategy buyDownFeeStrategy; private LoanBuyDownFeeIncomeType buyDownFeeIncomeType; + private boolean merchantBuyDownFee; private LoanApplicationTerms(Builder builder) { this.currency = builder.currency; @@ -290,6 +291,7 @@ public final class LoanApplicationTerms { this.buyDownFeeCalculationType = builder.buyDownFeeCalculationType; this.buyDownFeeStrategy = builder.buyDownFeeStrategy; this.buyDownFeeIncomeType = builder.buyDownFeeIncomeType; + this.merchantBuyDownFee = builder.merchantBuyDownFee; this.interestMethod = builder.interestMethod; this.allowPartialPeriodInterestCalcualtion = builder.allowPartialPeriodInterestCalculation; } @@ -330,6 +332,7 @@ public final class LoanApplicationTerms { private LoanBuyDownFeeCalculationType buyDownFeeCalculationType; private LoanBuyDownFeeStrategy buyDownFeeStrategy; private LoanBuyDownFeeIncomeType buyDownFeeIncomeType; + private boolean merchantBuyDownFee; private boolean allowPartialPeriodInterestCalculation; public Builder interestMethod(InterestMethod interestMethod) { @@ -492,6 +495,11 @@ public final class LoanApplicationTerms { return this; } + public Builder merchantBuyDownFee(boolean value) { + this.merchantBuyDownFee = value; + return this; + } + public LoanApplicationTerms build() { return new LoanApplicationTerms(this); } @@ -570,7 +578,8 @@ public final class LoanApplicationTerms { final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType, final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, final boolean enableBuyDownFee, final LoanBuyDownFeeCalculationType buyDownFeeCalculationType, - final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType) { + final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType, + final boolean merchantBuyDownFee) { final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; @@ -592,7 +601,7 @@ public final class LoanApplicationTerms { fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } @@ -670,7 +679,8 @@ public final class LoanApplicationTerms { loanProductRelatedDetail.isEnableIncomeCapitalization(), loanProductRelatedDetail.getCapitalizedIncomeCalculationType(), loanProductRelatedDetail.getCapitalizedIncomeStrategy(), loanProductRelatedDetail.getCapitalizedIncomeType(), loanProductRelatedDetail.isEnableBuyDownFee(), loanProductRelatedDetail.getBuyDownFeeCalculationType(), - loanProductRelatedDetail.getBuyDownFeeStrategy(), loanProductRelatedDetail.getBuyDownFeeIncomeType()); + loanProductRelatedDetail.getBuyDownFeeStrategy(), loanProductRelatedDetail.getBuyDownFeeIncomeType(), + loanProductRelatedDetail.isMerchantBuyDownFee()); } private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency, @@ -705,7 +715,8 @@ public final class LoanApplicationTerms { final boolean enableIncomeCapitalization, final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType, final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, final boolean enableBuyDownFee, final LoanBuyDownFeeCalculationType buyDownFeeCalculationType, - final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType) { + final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType, + final boolean merchantBuyDownFee) { this.currency = currency; this.loanTermFrequency = loanTermFrequency; @@ -815,6 +826,7 @@ public final class LoanApplicationTerms { this.buyDownFeeCalculationType = buyDownFeeCalculationType; this.buyDownFeeStrategy = buyDownFeeStrategy; this.buyDownFeeIncomeType = buyDownFeeIncomeType; + this.merchantBuyDownFee = merchantBuyDownFee; } public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate, @@ -1679,7 +1691,7 @@ public final class LoanApplicationTerms { this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy, this.enableIncomeCapitalization, this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy, this.capitalizedIncomeType, this.installmentAmountInMultiplesOf, this.enableBuyDownFee, this.buyDownFeeCalculationType, - this.buyDownFeeStrategy, this.buyDownFeeIncomeType); + this.buyDownFeeStrategy, this.buyDownFeeIncomeType, this.merchantBuyDownFee); } public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java index 293efcf34..c764ccff9 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java @@ -59,13 +59,13 @@ public class LoanAccountingBridgeMapper { currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), isAccountTransfer, false, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactionsBeforeChargeOff); + newLoanTransactionsBeforeChargeOff, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); AccountingBridgeDataDTO afterChargeOff = new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), isAccountTransfer, true, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactionsAfterChargeOff); + newLoanTransactionsAfterChargeOff, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); List result = new ArrayList<>(); result.add(beforeChargeOff); @@ -86,7 +86,7 @@ public class LoanAccountingBridgeMapper { loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), isAccountTransfer, loan.isChargedOff(), loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactions); + newLoanTransactions, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); } public AccountingBridgeLoanTransactionDTO mapToLoanTransactionData(final LoanTransaction transaction, final String currencyCode) { 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 a08c767bf..7a3784a39 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 @@ -183,4 +183,5 @@ public interface LoanProductConstants { String BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME = "buyDownFeeCalculationType"; String BUY_DOWN_FEE_STRATEGY_PARAM_NAME = "buyDownFeeStrategy"; String BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME = "buyDownFeeIncomeType"; + String MERCHANT_BUY_DOWN_FEE_PARAM_NAME = "merchantBuyDownFee"; } 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 87a673292..00fc321ae 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 @@ -213,6 +213,8 @@ public final class LoanProductsApiResourceSwagger { public String buyDownFeeStrategy; @Schema(example = "FEE", allowableValues = { "FEE", "INTEREST" }) public String buyDownFeeIncomeType; + @Schema(example = "false") + public Boolean merchantBuyDownFee; // Interest Recalculation @Schema(example = "false") @@ -693,6 +695,8 @@ public final class LoanProductsApiResourceSwagger { public StringEnumOptionData buyDownFeeStrategy; @Schema(example = "FEE") public StringEnumOptionData buyDownFeeIncomeType; + @Schema(example = "false") + public Boolean merchantBuyDownFee; } @Schema(description = "GetLoanProductsTemplateResponse") @@ -1157,6 +1161,8 @@ public final class LoanProductsApiResourceSwagger { public StringEnumOptionData buyDownFeeCalculationType; @Schema(example = "EQUAL_AMORTIZATION") public StringEnumOptionData buyDownFeeStrategy; + @Schema(example = "false") + public Boolean merchantBuyDownFee; @Schema(example = "FEE") public StringEnumOptionData buyDownFeeIncomeType; public List buyDownFeeCalculationTypeOptions; @@ -1505,6 +1511,8 @@ public final class LoanProductsApiResourceSwagger { public StringEnumOptionData buyDownFeeStrategy; @Schema(example = "FEE") public StringEnumOptionData buyDownFeeIncomeType; + @Schema(example = "false") + public Boolean merchantBuyDownFee; public List buyDownFeeCalculationTypeOptions; public List buyDownFeeStrategyOptions; public List buyDownFeeIncomeTypeOptions; @@ -1787,6 +1795,8 @@ public final class LoanProductsApiResourceSwagger { public String buyDownFeeStrategy; @Schema(example = "FEE", allowableValues = { "FEE", "INTEREST" }) public String buyDownFeeIncomeType; + @Schema(example = "false") + public Boolean merchantBuyDownFee; } 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 f7da2ba2b..ff33b5f51 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 @@ -256,6 +256,7 @@ public class LoanProductData implements Serializable { private StringEnumOptionData buyDownFeeCalculationType; private StringEnumOptionData buyDownFeeStrategy; private StringEnumOptionData buyDownFeeIncomeType; + private boolean merchantBuyDownFee; private List buyDownFeeCalculationTypeOptions; private List buyDownFeeStrategyOptions; private List buyDownFeeIncomeTypeOptions; @@ -371,6 +372,7 @@ public class LoanProductData implements Serializable { final StringEnumOptionData buyDownFeeCalculationType = null; final StringEnumOptionData buyDownFeeStrategy = null; final StringEnumOptionData buyDownFeeIncomeType = null; + final boolean merchantBuyDownFee = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -394,7 +396,7 @@ public class LoanProductData implements Serializable { loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } @@ -507,6 +509,7 @@ public class LoanProductData implements Serializable { final StringEnumOptionData buyDownFeeCalculationType = null; final StringEnumOptionData buyDownFeeStrategy = null; final StringEnumOptionData buyDownFeeIncomeType = null; + final boolean merchantBuyDownFee = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -530,7 +533,7 @@ public class LoanProductData implements Serializable { loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } @@ -650,6 +653,7 @@ public class LoanProductData implements Serializable { final StringEnumOptionData buyDownFeeCalculationType = null; final StringEnumOptionData buyDownFeeStrategy = null; final StringEnumOptionData buyDownFeeIncomeType = null; + final boolean merchantBuyDownFee = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -673,7 +677,7 @@ public class LoanProductData implements Serializable { loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } @@ -787,6 +791,7 @@ public class LoanProductData implements Serializable { final StringEnumOptionData buyDownFeeCalculationType = null; final StringEnumOptionData buyDownFeeStrategy = null; final StringEnumOptionData buyDownFeeIncomeType = null; + final boolean merchantBuyDownFee = false; return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -810,7 +815,7 @@ public class LoanProductData implements Serializable { loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -869,7 +874,7 @@ public class LoanProductData implements Serializable { final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy, final StringEnumOptionData capitalizedIncomeType, final boolean enableBuyDownFee, final StringEnumOptionData buyDownFeeCalculationType, final StringEnumOptionData buyDownFeeStrategy, - final StringEnumOptionData buyDownFeeIncomeType) { + final StringEnumOptionData buyDownFeeIncomeType, final boolean merchantBuyDownFee) { this.id = id; this.name = name; this.shortName = shortName; @@ -934,6 +939,7 @@ public class LoanProductData implements Serializable { this.buyDownFeeCalculationType = buyDownFeeCalculationType; this.buyDownFeeStrategy = buyDownFeeStrategy; this.buyDownFeeIncomeType = buyDownFeeIncomeType; + this.merchantBuyDownFee = merchantBuyDownFee; this.chargeOptions = null; this.penaltyOptions = null; @@ -1238,6 +1244,8 @@ public class LoanProductData implements Serializable { this.buyDownFeeCalculationTypeOptions = buyDownFeeCalculationTypeOptions; this.buyDownFeeStrategyOptions = buyDownFeeStrategyOptions; this.buyDownFeeIncomeTypeOptions = buyDownFeeIncomeTypeOptions; + + this.merchantBuyDownFee = productData.isMerchantBuyDownFee(); } private Collection nullIfEmpty(final Collection 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 c53c0f6b9..b4d3da989 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 @@ -287,7 +287,8 @@ public class LoanProduct extends AbstractPersistableCustom { final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType, final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, final boolean enableBuyDownFee, final LoanBuyDownFeeCalculationType buyDownFeeCalculationType, - final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType) { + final LoanBuyDownFeeStrategy buyDownFeeStrategy, final LoanBuyDownFeeIncomeType buyDownFeeIncomeType, + final boolean merchantBuyDownFee) { this.fund = fund; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode; @@ -339,7 +340,8 @@ public class LoanProduct extends AbstractPersistableCustom { enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, - installmentAmountInMultiplesOf, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + installmentAmountInMultiplesOf, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, + merchantBuyDownFee); 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 dcbf1a904..3c9458c3c 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 @@ -204,6 +204,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche @Column(name = "buy_down_fee_income_type") private LoanBuyDownFeeIncomeType buyDownFeeIncomeType; + @Column(name = "is_merchant_buy_down_fee") + private boolean merchantBuyDownFee = true; + @Column(name = "installment_amount_in_multiples_of") private Integer installmentAmountInMultiplesOf; @@ -226,7 +229,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, final Integer installmentAmountInMultiplesOf, final boolean enableBuyDownFee, final LoanBuyDownFeeCalculationType buyDownFeeCalculationType, final LoanBuyDownFeeStrategy buyDownFeeStrategy, - final LoanBuyDownFeeIncomeType buyDownFeeIncomeType) { + final LoanBuyDownFeeIncomeType buyDownFeeIncomeType, final boolean merchantBuyDownFee) { final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData); return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, @@ -238,7 +241,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, installmentAmountInMultiplesOf, - enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } protected LoanProductRelatedDetail() { @@ -264,7 +267,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, final LoanCapitalizedIncomeType capitalizedIncomeType, final Integer installmentAmountInMultiplesOf, final boolean enableBuyDownFee, final LoanBuyDownFeeCalculationType buyDownFeeCalculationType, final LoanBuyDownFeeStrategy buyDownFeeStrategy, - final LoanBuyDownFeeIncomeType buyDownFeeIncomeType) { + final LoanBuyDownFeeIncomeType buyDownFeeIncomeType, final boolean merchantBuyDownFee) { this.currency = currency; this.principal = defaultPrincipal; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; @@ -311,6 +314,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche this.buyDownFeeCalculationType = buyDownFeeCalculationType; this.buyDownFeeStrategy = buyDownFeeStrategy; this.buyDownFeeIncomeType = buyDownFeeIncomeType; + this.merchantBuyDownFee = merchantBuyDownFee; } private Integer defaultToNullIfZero(final Integer value) { diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml index fc4f0c9f7..b805e53b7 100644 --- a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml @@ -53,4 +53,5 @@ + diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1031_loan_merchant_buy_down_fee.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1031_loan_merchant_buy_down_fee.xml new file mode 100644 index 000000000..df6237ac2 --- /dev/null +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1031_loan_merchant_buy_down_fee.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/LoanDTO.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/LoanDTO.java index abe9389f9..925448d54 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/LoanDTO.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/data/LoanDTO.java @@ -47,4 +47,5 @@ public class LoanDTO { private boolean markedAsFraud; private Long chargeOffReasonCodeValue; private boolean markedAsWrittenOff; + private boolean merchantBuyDownFee; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java index 6f177f084..8e984cede 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java @@ -111,6 +111,7 @@ public class AccountingProcessorHelper { final boolean cashBasedAccountingEnabled = accountingBridgeData.isCashBasedAccountingEnabled(); final boolean upfrontAccrualBasedAccountingEnabled = accountingBridgeData.isUpfrontAccrualBasedAccountingEnabled(); final boolean periodicAccrualBasedAccountingEnabled = accountingBridgeData.isPeriodicAccrualBasedAccountingEnabled(); + final boolean merchantBuyDownFee = accountingBridgeData.isMerchantBuyDownFee(); final List loanTransactionDTOs = accountingBridgeData.getNewLoanTransactions(); @@ -170,7 +171,7 @@ public class AccountingProcessorHelper { return new LoanDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, upfrontAccrualBasedAccountingEnabled, periodicAccrualBasedAccountingEnabled, newLoanTransactions, isLoanMarkedAsChargeOff, isLoanMarkedAsFraud, - chargeOffReasonCodeValue, isLoanMarkedAsWrittenOff); + chargeOffReasonCodeValue, isLoanMarkedAsWrittenOff, merchantBuyDownFee); } public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Long loanProductId, PortfolioProductType productType, diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java index c86305d31..12b1e85d2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java @@ -435,8 +435,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal amount = loanTransactionDTO.getAmount(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); + final AccrualAccountsForLoan debitAccountType = loanDTO.isMerchantBuyDownFee() ? AccrualAccountsForLoan.BUY_DOWN_EXPENSE + : AccrualAccountsForLoan.FUND_SOURCE; if (MathUtil.isGreaterThanZero(amount)) { - this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, debitAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, amount); } 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 4b9ce6fab..a40399c2c 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 @@ -41,6 +41,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.SavingsPro import org.apache.fineract.accounting.producttoaccountmapping.service.ShareProductToGLAccountMappingHelper; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -61,6 +62,14 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc final JsonElement element = this.fromApiJsonHelper.parse(command.json()); final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId); + boolean merchantBuyDownFee = true; + if (fromApiJsonHelper.parameterExists(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME, element)) { + final Boolean merchantBuyDownFeeParamValue = fromApiJsonHelper + .extractBooleanNamed(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME, element); + if (merchantBuyDownFeeParamValue != null) { + merchantBuyDownFee = merchantBuyDownFeeParamValue; + } + } switch (accountingRuleType) { case NONE: @@ -207,9 +216,11 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element, LoanProductAccountingParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(), loanProductId, AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue()); - this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element, - LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), loanProductId, - AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue()); + if (merchantBuyDownFee) { + this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element, + LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), loanProductId, + AccrualAccountsForLoan.BUY_DOWN_EXPENSE.getValue()); + } // liabilities this.loanProductToGLAccountMappingHelper.saveLoanToLiabilityAccountMapping(element, @@ -370,7 +381,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc @Transactional public Map updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command, final boolean accountingRuleChanged, final AccountingRuleType accountingRuleType, final boolean enableIncomeCapitalization, - final boolean enableBuyDownFee) { + final boolean enableBuyDownFee, final boolean merchantBuyDownFee) { /*** * Variable tracks all accounting mapping properties that have been updated ***/ @@ -390,7 +401,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc } /*** else examine and update individual changes ***/ else { this.loanProductToGLAccountMappingHelper.handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element, - accountingRuleType, enableIncomeCapitalization, enableBuyDownFee); + accountingRuleType, enableIncomeCapitalization, enableBuyDownFee, merchantBuyDownFee); this.loanProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, loanProductId, changes); this.loanProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, loanProductId, changes); this.loanProductToGLAccountMappingHelper.updateChargeOffReasonToExpenseAccountMappings(command, element, loanProductId, 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 7a58ae8b8..51cc48183 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 @@ -554,7 +554,8 @@ public class LoanScheduleAssembler { loanProduct.getLoanProductRelatedDetail().isEnableBuyDownFee(), loanProduct.getLoanProductRelatedDetail().getBuyDownFeeCalculationType(), loanProduct.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), - loanProduct.getLoanProductRelatedDetail().getBuyDownFeeIncomeType()); + loanProduct.getLoanProductRelatedDetail().getBuyDownFeeIncomeType(), + loanProduct.getLoanProductRelatedDetail().isMerchantBuyDownFee()); } private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java index cd171730c..a1c09c258 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java @@ -445,7 +445,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(), loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false, - false, false, null, false, newTransactionDTOs); + false, false, null, false, newTransactionDTOs, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } } 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 5bcb32f48..fcdc4a5f2 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 @@ -327,6 +327,8 @@ public class LoanProductAssembler { .enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanBuyDownFeeStrategy.class); final LoanBuyDownFeeIncomeType buyDownFeeIncomeType = command .enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, LoanBuyDownFeeIncomeType.class); + final boolean merchantBuyDownFee = command + .booleanPrimitiveValueOfParameterNamed(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME); return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, @@ -349,7 +351,8 @@ public class LoanProductAssembler { repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, - capitalizedIncomeType, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + capitalizedIncomeType, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, + merchantBuyDownFee); } 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 aefe4f8bf..c1842b3e7 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 @@ -359,6 +359,13 @@ public class LoanProductRelatedDetailUpdateUtil { loanRepaymentScheduleDetail.setBuyDownFeeIncomeType(newValue); } + if (command.isChangeInBooleanParameterNamed(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME, + loanRepaymentScheduleDetail.isMerchantBuyDownFee())) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME); + actualChanges.put(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME, newValue); + loanRepaymentScheduleDetail.setMerchantBuyDownFee(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 e3f400916..706077e62 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 @@ -847,9 +847,9 @@ 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.enable_buy_down_fee as enableBuyDownFee, " + " l.buy_down_fee_calculation_type as buyDownFeeCalculationType, " - + " l.buy_down_fee_strategy as buyDownFeeStrategy, " + " l.buy_down_fee_income_type as buyDownFeeIncomeType, " + + " l.capitalized_income_type as capitalizedIncomeType, l.is_merchant_buy_down_fee as merchantBuyDownFee, " // + + " l.enable_buy_down_fee as enableBuyDownFee, l.buy_down_fee_calculation_type as buyDownFeeCalculationType, " + + " l.buy_down_fee_strategy as buyDownFeeStrategy, l.buy_down_fee_income_type as buyDownFeeIncomeType, " + " 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, " @@ -1246,6 +1246,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa rs.getString("buyDownFeeStrategy")); final StringEnumOptionData buyDownFeeIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeIncomeType.class, rs.getString("buyDownFeeIncomeType")); + final boolean merchantBuyDownFee = rs.getBoolean("merchantBuyDownFee"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, @@ -1267,7 +1268,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee, - buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, merchantBuyDownFee); } } 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 bad51f132..03841e783 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 @@ -201,7 +201,8 @@ public final class LoanProductDataValidator { LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, - LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue())); + LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(), + LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME)); private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, @@ -2838,14 +2839,21 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME).value(buyDownFeeIncomeType) .isOneOfEnumValues(LoanBuyDownFeeIncomeType.class) .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true); + Boolean merchantBuyDownFee = this.fromApiJsonHelper + .extractBooleanNamed(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME, element); + baseDataValidator.reset().parameter(LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME).value(merchantBuyDownFee) + .ignoreIfNull().validateForBooleanValue(); + // Accounting if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) { - final Long deferredIncomeLiabilityAccountId = this.fromApiJsonHelper - .extractLongNamed(LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), element); - baseDataValidator.reset().parameter(LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue()) - .value(deferredIncomeLiabilityAccountId).notNull() - .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true) - .integerGreaterThanZero(); + if (merchantBuyDownFee) { + final Long deferredIncomeLiabilityAccountId = this.fromApiJsonHelper + .extractLongNamed(LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), element); + baseDataValidator.reset().parameter(LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue()) + .value(deferredIncomeLiabilityAccountId).notNull() + .cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true) + .integerGreaterThanZero(); + } final Long incomeFromDeferredIncomeAccountId = this.fromApiJsonHelper .extractLongNamed(LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(), element); baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue()) 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 eb0f838b9..37e9987e2 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 @@ -299,7 +299,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo + "lp.enable_income_capitalization as enableIncomeCapitalization, " // + "lp.capitalized_income_calculation_type as capitalizedIncomeCalculationType, " // + "lp.capitalized_income_strategy as capitalizedIncomeStrategy, " // - + "lp.capitalized_income_type as capitalizedIncomeType, " // + + "lp.capitalized_income_type as capitalizedIncomeType, lp.is_merchant_buy_down_fee as merchantBuyDownFee, " // + "lp.enable_buy_down_fee as enableBuyDownFee, " + "lp.buy_down_fee_calculation_type as buyDownFeeCalculationType, " + "lp.buy_down_fee_strategy as buyDownFeeStrategy, " + "lp.buy_down_fee_income_type as buyDownFeeIncomeType " + " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id " @@ -579,6 +579,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo rs.getString("buyDownFeeStrategy")); final StringEnumOptionData buyDownFeeIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeIncomeType.class, rs.getString("buyDownFeeIncomeType")); + final boolean merchantBuyDownFee = rs.getBoolean("merchantBuyDownFee"); return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, @@ -604,7 +605,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy, - capitalizedIncome, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType); + capitalizedIncome, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType, + merchantBuyDownFee); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index 0b335e309..3b78f94d0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -263,6 +263,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro enableIncomeCapitalization = (boolean) changes.get(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME); } boolean enableBuyDownFee = product.getLoanProductRelatedDetail().isEnableBuyDownFee(); + boolean merchantBuyDownFee = product.getLoanProductRelatedDetail().isMerchantBuyDownFee(); if (changes.containsKey(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME)) { enableBuyDownFee = (boolean) changes.get(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME); } @@ -271,7 +272,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro final boolean accountingTypeChanged = changes.containsKey("accountingRule"); final Map accountingMappingChanges = this.accountMappingWritePlatformService .updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged, product.getAccountingRule(), - enableIncomeCapitalization, enableBuyDownFee); + enableIncomeCapitalization, enableBuyDownFee, merchantBuyDownFee); changes.putAll(accountingMappingChanges); if (changes.containsKey(LoanProductConstants.RATES_PARAM_NAME)) { diff --git a/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java b/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java index 1e8ddf379..52a3331f6 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java @@ -72,7 +72,8 @@ class CreateJournalEntriesForChargeOffLoanTest { transactionType, new BigDecimal("500.00"), new BigDecimal("500.00"), null, null, null, null, false, Collections.emptyList(), Collections.emptyList(), false, "", null, null, null, null); - loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasonId, false); + loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasonId, false, + false); } @Test 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 a8ab8a1cb..f929fdc8b 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 @@ -91,7 +91,7 @@ public class DefaultScheduledDateGeneratorTest { 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, - null, false, null, null, null); + null, false, null, null, null, false); // when List result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate, @@ -172,7 +172,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, null, false, null, null, null); + false, null, false, null, null, null, false, null, null, null, false); } private HolidayDetailDTO createHolidayDTO() { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java index 0119de847..955a9f0f8 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java @@ -271,7 +271,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest { .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()) .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()).loanScheduleType("PROGRESSIVE") - .loanScheduleProcessingType("HORIZONTAL").enableBuyDownFee(true) + .loanScheduleProcessingType("HORIZONTAL").enableBuyDownFee(true).merchantBuyDownFee(true) .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).locale("en").dateFormat("dd MMMM yyyy"); @@ -496,7 +496,7 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest { .enableBuyDownFee(true).buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue())); @@ -865,4 +865,44 @@ public class LoanBuyDownFeeTest extends BaseLoanIntegrationTest { }); } + @Test + public void testBuyDownFeeForNonMerchant() { + runAt("10 September 2024", () -> { + deleteAllExternalEvents(); + // Add initial buy down fee + + final PostLoanProductsResponse loanProductsResponse = loanProductHelper + .createLoanProduct(createProgressiveLoanProductWithBuyDownFee().merchantBuyDownFee(false)); + + // Apply for the loan with proper progressive loan settings + PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId, + loanProductsResponse.getResourceId(), "01 September 2024", 1000.0, 10.0, 12, null)); + loanId = postLoansResponse.getLoanId(); + loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 September 2024")); + disburseLoan(loanId, BigDecimal.valueOf(1000.0), "01 September 2024"); + + final Long buyDownFeeTransactionId = addBuyDownFeeForLoan(loanId, 400.0, "10 September 2024"); + assertNotNull(buyDownFeeTransactionId); + + verifyTransactions(loanId, // + transaction(1000.0, "Disbursement", "01 September 2024"), // + transaction(400.0, "Buy Down Fee", "10 September 2024")); + + // Verify initial buy down fee (non merchant) accounting entries + verifyTRJournalEntries(buyDownFeeTransactionId, debit(fundSource, 400.0), credit(deferredIncomeLiabilityAccount, 400.0)); + + // Reverse Buy Down Fee (non merchant) + PostLoansLoanIdTransactionsResponse transactionsResponse = loanTransactionHelper.reverseLoanTransaction(loanId, + buyDownFeeTransactionId, new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN) + .transactionDate("10 September 2024").note("Buy Down Fee reversed").transactionAmount(0.0).locale("en")); + Assertions.assertNotNull(transactionsResponse); + Assertions.assertNotNull(transactionsResponse.getResourceId()); + Assertions.assertEquals(transactionsResponse.getResourceId(), buyDownFeeTransactionId); + + // Verify initial buy down fee (non merchant) reversed accounting entries + verifyTRJournalEntries(buyDownFeeTransactionId, debit(fundSource, 400.0), credit(deferredIncomeLiabilityAccount, 400.0), + credit(fundSource, 400.0), debit(deferredIncomeLiabilityAccount, 400.0)); + }); + } + } 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 044cb49fc..28c144f26 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 @@ -259,7 +259,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { .enableBuyDownFee(true).buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue())); final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper @@ -339,7 +339,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { .enableBuyDownFee(true).buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue())); final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper @@ -404,7 +404,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()))); } @@ -414,7 +414,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { () -> loanProductHelper.createLoanProduct(create4IProgressive().enableBuyDownFee(true) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()))); } @@ -424,7 +424,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { () -> loanProductHelper.createLoanProduct(create4IProgressive().enableBuyDownFee(true) .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()))); } @@ -434,7 +434,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { () -> loanProductHelper.createLoanProduct(create4IProgressive().enableBuyDownFee(true) .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) - .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()) + .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()))); } @@ -444,7 +444,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { () -> loanProductHelper.createLoanProduct(create4IProgressive().enableBuyDownFee(true) .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) - .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) + .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).merchantBuyDownFee(true) .incomeFromBuyDownAccountId(feeIncomeAccount.getAccountID().longValue()))); } @@ -454,7 +454,7 @@ public class LoanProductTest extends BaseLoanIntegrationTest { () -> loanProductHelper.createLoanProduct(create4IProgressive().enableBuyDownFee(true) .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT) .buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION) - .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE) + .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE).merchantBuyDownFee(true) .buyDownExpenseAccountId(buyDownExpenseAccount.getAccountID().longValue()))); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java index 8bca2e261..7e24d12c5 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java @@ -206,7 +206,7 @@ public class LoanTransactionTest extends BaseLoanIntegrationTest { final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive() .enableBuyDownFee(true).buyDownFeeStrategy(PostLoanProductsRequest.BuyDownFeeStrategyEnum.EQUAL_AMORTIZATION)// .buyDownFeeCalculationType(PostLoanProductsRequest.BuyDownFeeCalculationTypeEnum.FLAT)// - .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.INTEREST)// + .buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.INTEREST).merchantBuyDownFee(true)// .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) // .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()) // .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE) // diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java index 0c0e0b614..43b34bf85 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java @@ -167,6 +167,7 @@ public class LoanProductTestBuilder { private String chargeOffBehaviour; private boolean interestRecognitionOnDisbursementDate = false; private Boolean enableBuyDownFee = false; + private Boolean merchantBuyDownFee = false; private String buyDownFeeCalculationType; public String build() { @@ -347,8 +348,8 @@ public class LoanProductTestBuilder { map.put("enableBuyDownFee", this.enableBuyDownFee); } - if (this.buyDownFeeCalculationType != null) { - map.put("buyDownFeeCalculationType", this.buyDownFeeCalculationType); + if (this.merchantBuyDownFee != null) { + map.put("merchantBuyDownFee", this.merchantBuyDownFee); } return map; @@ -921,6 +922,11 @@ public class LoanProductTestBuilder { return this; } + public LoanProductTestBuilder withMerchantBuyDownFee(final Boolean merchantBuyDownFee) { + this.merchantBuyDownFee = merchantBuyDownFee; + return this; + } + public LoanProductTestBuilder withBuyDownFeeCalculationType(final String buyDownFeeCalculationType) { this.buyDownFeeCalculationType = buyDownFeeCalculationType; return this;