FINERACT-2348: Refactor Capitalized income / Buydown fee amortization allocation calculation

This commit is contained in:
mariiaKraievska
2025-09-10 12:30:38 +02:00
committed by Adam Saghy
parent 719cba74c2
commit 8da0842f87
7 changed files with 490 additions and 112 deletions
@@ -69,6 +69,11 @@ public class LoanAmortizationAllocationServiceImpl implements LoanAmortizationAl
return retrieveLoanAmortizationAllocationData(loanTransaction, loanId);
}
@Override
public BigDecimal calculateAlreadyAmortizedAmount(final Long loanTransactionId, final Long loanId) {
return loanAmortizationAllocationMappingRepository.calculateAlreadyAmortizedAmount(loanTransactionId, loanId);
}
private LoanAmortizationAllocationData retrieveLoanAmortizationAllocationData(final LoanTransaction loanTransaction,
final Long loanId) {
try {
@@ -34,7 +34,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
@@ -75,7 +74,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
Money totalAmortization = Money.zero(loan.getCurrency());
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
BigDecimal totalAmortizedAccumulator = totalAmortized;
for (LoanBuyDownFeeBalance balance : balances) {
BigDecimal amortizationAmount;
AmortizationType amortizationType;
@@ -84,46 +82,26 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), tillDatePlusOne, loan.getCurrency());
totalAmortization = totalAmortization.add(amortizationTillDate);
final BigDecimal alreadyAmortizedAmount = loanAmortizationAllocationService
.calculateAlreadyAmortizedAmount(balance.getLoanTransaction().getId(), loan.getId());
if (!adjustments.isEmpty()) {
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
loan.getId());
BigDecimal amortizedAmount = BigDecimal.ZERO;
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
.getAmortizationMappings()) {
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
}
}
amortizationAmount = amortizedAmount;
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else {
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
} else {
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(amortizationTillDate.getAmount()));
balance.setUnrecognizedAmount(
MathUtil.subtract(balance.getAmount(), balance.getAmountAdjustment(), amortizationTillDate.getAmount()));
} else {
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
amortizationType = AmortizationType.AM_ADJ;
balance.setClosed(true);
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
}
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
@@ -236,7 +214,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
BigDecimal totalAmortization = BigDecimal.ZERO;
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan);
BigDecimal totalAmortizedAccumulator = totalAmortized;
for (LoanBuyDownFeeBalance balance : balances) {
BigDecimal amortizationAmount;
AmortizationType amortizationType;
@@ -246,38 +223,19 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
final Money amortizationTillDate = BuyDownFeeAmortizationUtil.calculateTotalAmortizationTillDate(balance, adjustments,
maturityDate, loan.getLoanProductRelatedDetail().getBuyDownFeeStrategy(), maturityDate, loan.getCurrency());
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
final BigDecimal alreadyAmortizedAmount = loanAmortizationAllocationService
.calculateAlreadyAmortizedAmount(balance.getLoanTransaction().getId(), loan.getId());
if (!adjustments.isEmpty()) {
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
.retrieveLoanAmortizationAllocationsForBuyDownFeeTransaction(balance.getLoanTransaction().getId(),
loan.getId());
BigDecimal amortizedAmount = BigDecimal.ZERO;
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
.getAmortizationMappings()) {
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
}
}
amortizationAmount = amortizedAmount;
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else {
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
} else {
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
if (isChargeOff) {
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
@@ -287,7 +245,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
amortizationType = AmortizationType.AM_ADJ;
balance.setClosed(true);
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
}
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
@@ -35,7 +35,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.data.LoanAmortizationAllocationData;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
@@ -119,7 +118,6 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
BigDecimal totalAmortization = BigDecimal.ZERO;
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
BigDecimal totalAmortizedAccumulator = totalAmortized;
for (LoanCapitalizedIncomeBalance balance : balances) {
BigDecimal amortizationAmount;
AmortizationType amortizationType;
@@ -130,38 +128,19 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), maturityDate,
loan.getCurrency());
totalAmortization = totalAmortization.add(amortizationTillDate.getAmount());
final BigDecimal alreadyAmortizedAmount = loanAmortizationAllocationService
.calculateAlreadyAmortizedAmount(balance.getLoanTransaction().getId(), loan.getId());
if (!adjustments.isEmpty()) {
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
loan.getId());
BigDecimal amortizedAmount = BigDecimal.ZERO;
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
.getAmortizationMappings()) {
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
}
}
amortizationAmount = amortizedAmount;
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else {
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
} else {
amortizationAmount = totalAmortization.subtract(totalAmortizedAccumulator);
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
if (isChargeOff) {
balance.setChargedOffAmount(balance.getUnrecognizedAmount());
@@ -171,7 +150,6 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
amortizationType = AmortizationType.AM_ADJ;
balance.setClosed(true);
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
}
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService
@@ -246,7 +224,6 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
final List<LoanAmortizationAllocationMapping> loanAmortizationAllocationMappings = new ArrayList<>();
Money totalAmortization = Money.zero(loan.getCurrency());
final BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountCapitalizedIncome(loan);
BigDecimal totalAmortizedAccumulator = totalAmortized;
for (LoanCapitalizedIncomeBalance balance : balances) {
BigDecimal amortizationAmount;
AmortizationType amortizationType;
@@ -256,46 +233,26 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
adjustments, maturityDate, loan.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(), tillDatePlusOne,
loan.getCurrency());
totalAmortization = totalAmortization.add(amortizationTillDate);
final BigDecimal alreadyAmortizedAmount = loanAmortizationAllocationService
.calculateAlreadyAmortizedAmount(balance.getLoanTransaction().getId(), loan.getId());
if (!adjustments.isEmpty()) {
BigDecimal alreadyAmortizedAmount = balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(MathUtil.nullToZero(balance.getChargedOffAmount())).subtract(balance.getUnrecognizedAmount());
if (alreadyAmortizedAmount.compareTo(BigDecimal.ZERO) == 0) {
final LoanAmortizationAllocationData loanAmortizationAllocationData = loanAmortizationAllocationService
.retrieveLoanAmortizationAllocationsForCapitalizedIncomeTransaction(balance.getLoanTransaction().getId(),
loan.getId());
BigDecimal amortizedAmount = BigDecimal.ZERO;
for (LoanAmortizationAllocationData.AmortizationMappingData loanAmortizationMapping : loanAmortizationAllocationData
.getAmortizationMappings()) {
if (AmortizationType.AM.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.add(loanAmortizationMapping.getAmount());
} else if (AmortizationType.AM_ADJ.equals(loanAmortizationMapping.getType())) {
amortizedAmount = amortizedAmount.subtract(loanAmortizationMapping.getAmount());
}
}
amortizationAmount = amortizedAmount;
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
if (alreadyAmortizedAmount.compareTo(amortizationTillDate.getAmount()) > 0) {
amortizationAmount = alreadyAmortizedAmount.subtract(amortizationTillDate.getAmount());
amortizationType = AmortizationType.AM_ADJ;
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
} else {
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
} else {
amortizationAmount = totalAmortization.getAmount().subtract(totalAmortizedAccumulator);
amortizationAmount = amortizationTillDate.getAmount().subtract(alreadyAmortizedAmount);
amortizationType = AmortizationType.AM;
totalAmortizedAccumulator = totalAmortizedAccumulator.add(amortizationAmount);
}
balance.setUnrecognizedAmount(balance.getAmount().subtract(MathUtil.nullToZero(balance.getAmountAdjustment()))
.subtract(amortizationTillDate.getAmount()));
balance.setUnrecognizedAmount(
MathUtil.subtract(balance.getAmount(), balance.getAmountAdjustment(), amortizationTillDate.getAmount()));
} else {
amortizationAmount = balance.getAmount().subtract(balance.getUnrecognizedAmount());
amortizationType = AmortizationType.AM_ADJ;
balance.setClosed(true);
totalAmortizedAccumulator = totalAmortizedAccumulator.subtract(amortizationAmount);
}
if (amortizationAmount.compareTo(BigDecimal.ZERO) > 0) {
final LoanAmortizationAllocationMapping loanAmortizationAllocationMapping = loanAmortizationAllocationService