FINERACT-2348: Accounting mismatch in case amortization and amortization adjustment occurs at the same time

This commit is contained in:
mariiaKraievska
2025-10-06 11:25:34 +02:00
committed by Adam Saghy
parent ceb9f0f783
commit 09bacb8827
5 changed files with 487 additions and 25 deletions
@@ -316,12 +316,25 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
if (mapping == null) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), creditAccountType.getValue(),
glAccountBalanceHolder);
}
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
mapping.getGlAccount(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), mapping.getGlAccount(),
glAccountBalanceHolder);
}
}
});
}
@@ -340,12 +353,25 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
if (mapping == null) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), creditAccountType.getValue(),
glAccountBalanceHolder);
}
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
mapping.getGlAccount(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), mapping.getGlAccount(),
glAccountBalanceHolder);
}
}
});
}
@@ -550,7 +576,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, creditAccountType.getValue(),
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
} else {
classificationCodeValues.stream().forEach(classificationCodeValue -> {
classificationCodeValues.forEach(classificationCodeValue -> {
ProductToGLAccountMapping mapping = null;
if (classificationCodeValue.getReferenceValueId() != null) {
mapping = fetchAdvanceAccountingMappingForCodeValue(loanProductId, classificationCodeValue.getReferenceValueId(),
@@ -558,12 +584,25 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
if (mapping == null) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), creditAccountType.getValue(),
glAccountBalanceHolder);
}
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
mapping.getGlAccount(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), mapping.getGlAccount(),
glAccountBalanceHolder);
}
}
});
}
@@ -582,12 +621,25 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
if (mapping == null) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
creditAccountType.getValue(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), creditAccountType.getValue(),
glAccountBalanceHolder);
}
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId, mapping.getGlAccount(),
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), glAccountBalanceHolder);
if (MathUtil.isGreaterThanZero(classificationCodeValue.getAmount())) {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount(), paymentTypeId,
mapping.getGlAccount(), AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(),
glAccountBalanceHolder);
} else {
populateCreditDebitMaps(loanProductId, classificationCodeValue.getAmount().negate(), paymentTypeId,
AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), mapping.getGlAccount(),
glAccountBalanceHolder);
}
}
});
}
@@ -898,6 +950,17 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
private void populateCreditDebitMaps(final Long loanProductId, final BigDecimal transactionPartAmount, final Long paymentTypeId,
final Integer creditAccountType, final GLAccount accountDebit, final GLAccountBalanceHolder glAccountBalanceHolder) {
if (MathUtil.isGreaterThanZero(transactionPartAmount)) {
// Resolve Credit
final GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, paymentTypeId);
glAccountBalanceHolder.addToCredit(accountCredit, transactionPartAmount);
// Resolve Debit
glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
}
}
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
final boolean isMarkedAsChargeOff = loanDTO.isMarkedAsChargeOff();
if (isMarkedAsChargeOff) {
@@ -91,6 +91,7 @@ import org.apache.fineract.portfolio.PortfolioProductType;
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO;
import org.apache.fineract.portfolio.loanaccount.domain.AmortizationType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMapping;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository;
@@ -881,18 +882,21 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
private List<AdvancedMappingtDTO> getLoanTransactionClassificationId(final LoanTransaction loanTransaction) {
List<AdvancedMappingtDTO> advancedMappingData = new ArrayList<AdvancedMappingtDTO>();
final List<AdvancedMappingtDTO> advancedMappingData = new ArrayList<AdvancedMappingtDTO>();
if (loanTransaction.isCapitalizedIncomeAmortization() || loanTransaction.isBuyDownFeeAmortization()) {
final List<LoanAmortizationAllocationMapping> loanTransactionAllocations = loanAmortizationAllocationMappingRepository
.fetchLoanTransactionAllocationByAmortizationLoanTransactionId(loanTransaction.getId(),
loanTransaction.getLoan().getId());
loanTransactionAllocations.stream().forEach(loanTransactionAllocation -> {
loanTransactionAllocations.forEach(loanTransactionAllocation -> {
final CodeValue classification = loanTransactionRepository
.fetchClassificationCodeValueByTransactionId(loanTransactionAllocation.getBaseLoanTransactionId());
final BigDecimal allocationAmount = loanTransactionAllocation.getAmortizationType().equals(AmortizationType.AM)
? loanTransactionAllocation.getAmount()
: loanTransactionAllocation.getAmount().negate();
if (classification != null) {
advancedMappingData.add(new AdvancedMappingtDTO(classification.getId(), loanTransactionAllocation.getAmount()));
advancedMappingData.add(new AdvancedMappingtDTO(classification.getId(), allocationAmount));
} else {
advancedMappingData.add(new AdvancedMappingtDTO(null, loanTransactionAllocation.getAmount()));
advancedMappingData.add(new AdvancedMappingtDTO(null, allocationAmount));
}
});
}