FINERACT-2347: Introduce classification field on loan transactions

This commit is contained in:
Jose Alberto Hernandez
2025-09-03 11:43:06 +02:00
committed by Adam Saghy
parent 9ba17bf79a
commit 65538495aa
27 changed files with 442 additions and 21 deletions
@@ -26,6 +26,7 @@ import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -40,6 +41,7 @@ import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -65,6 +67,7 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
private final ExternalIdFactory externalIdFactory;
private final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository;
private final BusinessEventNotifierService businessEventNotifierService;
private final CodeValueRepository codeValueRepository;
@Transactional
@Override
@@ -93,6 +96,9 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
// Add to loan (NO schedule recalculation as per requirements)
loan.addLoanTransaction(buyDownFeeTransaction);
// Add Loan Transaction classification
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.BUY_DOWN_FEE_CLASSIFICATION_CODE, buyDownFeeTransaction);
// Save transaction
loanTransactionRepository.saveAndFlush(buyDownFeeTransaction);
@@ -150,6 +156,8 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
buyDownFeeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(buyDownFeeAdjustment,
originalBuyDownFee.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
// Inherit from the target transaction the classification
buyDownFeeAdjustment.setClassification(originalBuyDownFee.get().getClassification());
// Add transaction to loan
loan.addLoanTransaction(buyDownFeeAdjustment);
@@ -203,4 +211,12 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer
buyDownFeeBalance.setUnrecognizedAmount(buyDownFeeTransaction.getAmount());
loanBuyDownFeeBalanceRepository.saveAndFlush(buyDownFeeBalance);
}
private void addClassificationCodeToTransaction(final JsonCommand command, final String codeName, LoanTransaction loanTransaction) {
final Long transactionClassificationId = command
.longValueOfParameterNamed(LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME);
if (transactionClassificationId != null) {
loanTransaction.setClassification(codeValueRepository.findByCodeNameAndId(codeName, transactionClassificationId));
}
}
}
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -37,6 +38,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeTransactionCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
@@ -66,6 +68,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
private final LoanBalanceService loanBalanceService;
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
private final BusinessEventNotifierService businessEventNotifierService;
private final CodeValueRepository codeValueRepository;
@Transactional
@Override
@@ -86,6 +89,9 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
transactionDate, txnExternalId);
// Update loan with capitalized income
loan.addLoanTransaction(capitalizedIncomeTransaction);
// Add Loan Transaction classification
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
capitalizedIncomeTransaction);
// Recalculate loan transactions
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeTransaction);
// Save and flush (PK is set)
@@ -137,6 +143,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
Money.of(loan.getCurrency(), transactionAmount), paymentDetail, transactionDate, txnExternalId);
capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
capitalizedIncomeAdjustment.setClassification(capitalizedIncome.get().getClassification());
loan.addLoanTransaction(capitalizedIncomeAdjustment);
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeAdjustment);
LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
@@ -205,4 +212,11 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
capitalizedIncomeBalanceRepository.saveAndFlush(capitalizedIncomeBalance);
}
private void addClassificationCodeToTransaction(final JsonCommand command, final String codeName, LoanTransaction loanTransaction) {
final Long transactionClassificationId = command
.longValueOfParameterNamed(LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME);
if (transactionClassificationId != null) {
loanTransaction.setClassification(codeValueRepository.findByCodeNameAndId(codeName, transactionClassificationId));
}
}
}
@@ -45,6 +45,7 @@ import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.portfolio.common.service.Validator;
import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionApiConstants;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
@@ -149,6 +150,11 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
}
}
final Long transactionClassificationId = fromApiJsonHelper
.extractLongNamed(LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME, element);
loanTransactionValidator.validateClassificationCodeValue(LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
transactionClassificationId, baseDataValidator);
validatePaymentDetails(baseDataValidator, element);
validateNote(baseDataValidator, element);
validateExternalId(baseDataValidator, element);
@@ -276,7 +282,8 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
}
private static final List<String> BUY_DOWN_FEE_TRANSACTION_SUPPORTED_PARAMETERS = List
.of(new String[] { "transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId" });
.of(new String[] { "transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId",
LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME });
@Override
public void validateBuyDownFee(JsonCommand command, Long loanId) {
@@ -313,6 +320,11 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount();
final Long transactionClassificationId = fromApiJsonHelper
.extractLongNamed(LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME, element);
loanTransactionValidator.validateClassificationCodeValue(LoanTransactionApiConstants.BUY_DOWN_FEE_CLASSIFICATION_CODE,
transactionClassificationId, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -576,9 +588,15 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
loanTransactionValidator.validateManualInterestRefundTransaction(json);
}
@Override
public void validateClassificationCodeValue(final String codeName, final Long transactionClassificationId,
DataValidatorBuilder baseDataValidator) {
loanTransactionValidator.validateClassificationCodeValue(codeName, transactionClassificationId, baseDataValidator);
}
private Set<String> getCapitalizedIncomeParameters() {
return new HashSet<>(
Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
return new HashSet<>(Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note",
"externalId", LoanTransactionApiConstants.TRANSACTION_CLASSIFICATIONID_PARAMNAME));
}
private Set<String> getCapitalizedIncomeAdjustmentParameters() {
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.portfolio.loanaccount.starter;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
@@ -57,11 +58,12 @@ public class ProgressiveLoanAccountConfiguration {
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService) {
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService,
CodeValueRepository codeValueRepository) {
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
businessEventNotifierService);
businessEventNotifierService, codeValueRepository);
}
@Bean
@@ -89,4 +91,5 @@ public class ProgressiveLoanAccountConfiguration {
LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository) {
return new CapitalizedIncomeBalanceReadServiceImpl(loanRepository, loanCapitalizedIncomeBalanceRepository);
}
}