FINERACT-2311: Add buydown fees to loan

This commit is contained in:
budaidev
2025-07-01 20:14:43 +02:00
committed by Adam Saghy
parent cb6a15c3d2
commit 98b7328508
23 changed files with 729 additions and 7 deletions
@@ -173,6 +173,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), element);
final Long incomeFromBuyDownFeesAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(), element);
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
final Long overPaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.OVERPAYMENT.getValue(),
@@ -198,12 +201,14 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
case ACCRUAL_PERIODIC:
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
incomeFromRecoveryAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
incomeFromRecoveryAccountId, incomeFromBuyDownFeesAccountId, receivableInterestAccountId, receivableFeeAccountId,
receivablePenaltyAccountId);
break;
case ACCRUAL_UPFRONT:
populateChangesForAccrualBasedAccounting(changes, fundAccountId, loanPortfolioAccountId, incomeFromInterestId,
incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId, overPaymentAccountId, transfersInSuspenseAccountId,
incomeFromRecoveryAccountId, receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
incomeFromRecoveryAccountId, incomeFromBuyDownFeesAccountId, receivableInterestAccountId, receivableFeeAccountId,
receivablePenaltyAccountId);
break;
}
@@ -213,8 +218,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
private void populateChangesForAccrualBasedAccounting(final Map<String, Object> changes, final Long fundAccountId,
final Long loanPortfolioAccountId, final Long incomeFromInterestId, final Long incomeFromFeeId, final Long incomeFromPenaltyId,
final Long writeOffAccountId, final Long overPaymentAccountId, final Long transfersInSuspenseAccountId,
final Long incomeFromRecoveryAccountId, final Long receivableInterestAccountId, final Long receivableFeeAccountId,
final Long receivablePenaltyAccountId) {
final Long incomeFromRecoveryAccountId, final Long incomeFromBuyDownFeesAccountId, final Long receivableInterestAccountId,
final Long receivableFeeAccountId, final Long receivablePenaltyAccountId) {
changes.put(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId);
changes.put(LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), receivableFeeAccountId);
@@ -181,6 +181,8 @@ public interface LoanApiConstants {
String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = "capitalizedIncomeAdjustment";
String CONTRACT_TERMINATION_COMMAND = "contractTermination";
String UNDO_CONTRACT_TERMINATION_COMMAND = "undoContractTermination";
String BUY_DOWN_FEE_COMMAND = "buyDownFee";
String BUY_DOWN_FEE_ADJUSTMENT_COMMAND = "buyDownFeeAdjustment";
// Data Validator names
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
@@ -72,6 +72,8 @@ public class LoanTransactionEnumData implements Serializable {
private final boolean capitalizedIncomeAdjustment;
private final boolean capitalizedIncomeAmortizationAdjustment;
private final boolean contractTermination;
private final boolean buyDownFee;
private final boolean buyDownFeeAdjustment;
public LoanTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
@@ -114,6 +116,8 @@ public class LoanTransactionEnumData implements Serializable {
this.capitalizedIncomeAmortizationAdjustment = Long
.valueOf(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue()).equals(this.id);
this.contractTermination = Long.valueOf(LoanTransactionType.CONTRACT_TERMINATION.getValue()).equals(this.id);
this.buyDownFee = Long.valueOf(LoanTransactionType.BUY_DOWN_FEE.getValue()).equals(this.id);
this.buyDownFeeAdjustment = Long.valueOf(LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getValue()).equals(this.id);
}
public boolean isRepaymentType() {
@@ -961,4 +961,13 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
this.dateOf = transactionDate;
}
public static LoanTransaction buyDownFee(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
final LocalDate transactionDate, final ExternalId externalId) {
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.BUY_DOWN_FEE, paymentDetail, amount.getAmount(),
transactionDate, externalId);
}
public boolean isBuyDownFee() {
return LoanTransactionType.BUY_DOWN_FEE.equals(this.typeOf);
}
}
@@ -73,6 +73,9 @@ public enum LoanTransactionType {
CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT(39, "loanTransactionType.capitalizedIncomeAmortizationAdjustment"), //
// Kind of Final Transactions
CONTRACT_TERMINATION(38, "loanTransactionType.contractTermination"), //
BUY_DOWN_FEE(40, "loanTransactionType.buyDownFee"), //
BUY_DOWN_FEE_ADJUSTMENT(41, "loanTransactionType.buyDownFeeAdjustment"), //
;
private final Integer value;
@@ -128,6 +131,8 @@ public enum LoanTransactionType {
case 37 -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
case 38 -> LoanTransactionType.CONTRACT_TERMINATION;
case 39 -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT;
case 40 -> LoanTransactionType.BUY_DOWN_FEE;
case 41 -> LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT;
default -> LoanTransactionType.INVALID;
};
}
@@ -256,4 +261,12 @@ public enum LoanTransactionType {
public boolean isContractTermination() {
return this == LoanTransactionType.CONTRACT_TERMINATION;
}
public boolean isBuyDownFee() {
return this == LoanTransactionType.BUY_DOWN_FEE;
}
public boolean isBuyDownFeeAdjustment() {
return this == LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT;
}
}
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when loan transaction processing violates a domain rule.
*/
public class LoanTransactionProcessingException extends AbstractPlatformDomainRuleException {
public LoanTransactionProcessingException(final String defaultUserMessage, final Object... defaultUserMessageArgs) {
super("error.msg.loan.transaction.processing", defaultUserMessage, defaultUserMessageArgs);
}
public LoanTransactionProcessingException(final String action, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super("error.msg.loan.transaction." + action, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -338,6 +338,10 @@ public final class LoanEnumerations {
case CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT -> new LoanTransactionEnumData(
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getValue().longValue(),
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT.getCode(), "Capitalized Income Amortization Adjustment");
case BUY_DOWN_FEE -> new LoanTransactionEnumData(LoanTransactionType.BUY_DOWN_FEE.getValue().longValue(),
LoanTransactionType.BUY_DOWN_FEE.getCode(), "Buy Down Fee");
case BUY_DOWN_FEE_ADJUSTMENT -> new LoanTransactionEnumData(LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getValue().longValue(),
LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT.getCode(), "Buy Down Fee Adjustment");
};
}