FINERACT-2081: Refactor GL account balance calculation

This commit is contained in:
Adam Saghy
2024-11-26 18:06:09 +01:00
parent ccfb8c3c9c
commit 2e89c40e6e
3 changed files with 139 additions and 102 deletions
@@ -0,0 +1,53 @@
/**
* 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.accounting.journalentry.data;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.Data;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
@Data
public class GLAccountBalanceHolder {
private final Map<Long, GLAccount> glAccountMap = new LinkedHashMap<>();
private final Map<Long, BigDecimal> debitBalances = new LinkedHashMap<>();
private final Map<Long, BigDecimal> creditBalances = new LinkedHashMap<>();
public void addToCredit(@NotNull GLAccount creditAccount, @NotNull BigDecimal amount) {
addToProperBalance(creditBalances, creditAccount, amount);
}
public void addToDebit(@NotNull GLAccount debitAccount, @NotNull BigDecimal amount) {
addToProperBalance(debitBalances, debitAccount, amount);
}
private void addToProperBalance(@NotNull Map<Long, BigDecimal> balanceMap, @NotNull @NotNull GLAccount account,
@NotNull BigDecimal amount) {
glAccountMap.putIfAbsent(account.getId(), account);
if (balanceMap.containsKey(account.getId())) {
BigDecimal totalAmount = balanceMap.get(account.getId()).add(amount);
balanceMap.put(account.getId(), totalAmount);
} else {
balanceMap.put(account.getId(), amount);
}
}
}
@@ -727,6 +727,16 @@ public class AccountingProcessorHelper {
} }
} }
public void createDebitJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final Long loanId,
final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal,
final GLAccount account) {
if (isReversal) {
createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount);
} else {
createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount);
}
}
public void createDebitJournalEntryOrReversalForLoanCharges(final Office office, final String currencyCode, public void createDebitJournalEntryOrReversalForLoanCharges(final Office office, final String currencyCode,
final int accountMappingTypeId, final Long loanProductId, final Long chargeId, final Long loanId, final String transactionId, final int accountMappingTypeId, final Long loanProductId, final Long chargeId, final Long loanId, final String transactionId,
final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) {
@@ -31,6 +31,7 @@ import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccounts
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity; import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO; import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
import org.apache.fineract.accounting.journalentry.data.GLAccountBalanceHolder;
import org.apache.fineract.accounting.journalentry.data.LoanDTO; import org.apache.fineract.accounting.journalentry.data.LoanDTO;
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO; import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.MathUtil;
@@ -135,10 +136,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal overPayment = loanTransactionDTO.getOverPayment(); final BigDecimal overPayment = loanTransactionDTO.getOverPayment();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
Map<GLAccount, BigDecimal> accountMapForCredit = new LinkedHashMap<>();
Map<Integer, BigDecimal> accountMapForDebit = new LinkedHashMap<>();
if (isMarkedAsChargeOff) { if (isMarkedAsChargeOff) {
// ChargeOFF // ChargeOFF
@@ -146,72 +144,72 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// interest payment // interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// handle fees payment // handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// handle penalty payment // handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// handle overpayment // handle overpayment
if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) { if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
} else { } else {
// principal payment // principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// interest payment // interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// handle fees payment // handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
// handle penalty payment // handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} }
// handle overpayment // handle overpayment
if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) { if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
} }
} }
// create credit entries // create credit entries
for (Map.Entry<GLAccount, BigDecimal> creditEntry : accountMapForCredit.entrySet()) { for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), isReversal, creditEntry.getKey()); creditEntry.getValue(), isReversal, glAccount);
} }
// create debit entries // create debit entries
for (Map.Entry<Integer, BigDecimal> debitEntry : accountMapForDebit.entrySet()) { for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, debitEntry.getKey().intValue(), loanProductId, GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue(), isReversal); this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
debitEntry.getValue(), isReversal, glAccount);
} }
} }
@@ -221,7 +219,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final Long loanId = loanDTO.getLoanId(); final Long loanId = loanDTO.getLoanId();
final String currencyCode = loanDTO.getCurrencyCode(); final String currencyCode = loanDTO.getCurrencyCode();
final boolean isMarkedFraud = loanDTO.isMarkedAsFraud(); final boolean isMarkedFraud = loanDTO.isMarkedAsFraud();
// transaction properties // transaction properties
final String transactionId = loanTransactionDTO.getTransactionId(); final String transactionId = loanTransactionDTO.getTransactionId();
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
@@ -231,74 +228,55 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
Map<GLAccount, BigDecimal> accountMapForCredit = new LinkedHashMap<>();
Map<Integer, BigDecimal> accountMapForDebit = new LinkedHashMap<>();
// principal payment // principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (isMarkedFraud) { if (isMarkedFraud) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), glAccountBalanceHolder);
} }
} }
// interest payment // interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), glAccountBalanceHolder);
} }
// handle fees payment // handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), glAccountBalanceHolder);
} }
// handle penalty payment // handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), glAccountBalanceHolder);
} }
// create credit entries // create credit entries
for (Map.Entry<GLAccount, BigDecimal> creditEntry : accountMapForCredit.entrySet()) { for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), isReversal, creditEntry.getKey()); creditEntry.getValue(), isReversal, glAccount);
} }
// create debit entries // create debit entries
for (Map.Entry<Integer, BigDecimal> debitEntry : accountMapForDebit.entrySet()) { for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, debitEntry.getKey().intValue(), loanProductId, GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue(), isReversal); this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
debitEntry.getValue(), isReversal, glAccount);
} }
} }
private void populateCreditDebitMaps(Long loanProductId, BigDecimal transactionPartAmount, Long paymentTypeId, private void populateCreditDebitMaps(Long loanProductId, BigDecimal transactionPartAmount, Long paymentTypeId,
Integer creditAccountType, Integer debitAccountType, Map<GLAccount, BigDecimal> accountMapForCredit, Integer creditAccountType, Integer debitAccountType, GLAccountBalanceHolder glAccountBalanceHolder) {
Map<Integer, BigDecimal> accountMapForDebit) { // Resolve Credit
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, paymentTypeId); GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, paymentTypeId);
if (accountMapForCredit.containsKey(accountCredit)) { glAccountBalanceHolder.addToCredit(accountCredit, transactionPartAmount);
BigDecimal amount = accountMapForCredit.get(accountCredit).add(transactionPartAmount); // Resolve Debit
accountMapForCredit.put(accountCredit, amount); GLAccount accountDebit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, debitAccountType, paymentTypeId);
} else { glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
accountMapForCredit.put(accountCredit, transactionPartAmount);
}
Integer accountDebit = returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, debitAccountType,
accountMapForDebit);
if (accountMapForDebit.containsKey(accountDebit)) {
BigDecimal amount = accountMapForDebit.get(accountDebit).add(transactionPartAmount);
accountMapForDebit.put(accountDebit, amount);
} else {
accountMapForDebit.put(accountDebit, transactionPartAmount);
}
} }
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) { private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
@@ -710,9 +688,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
Map<GLAccount, BigDecimal> accountMapForCredit = new LinkedHashMap<>();
Map<Integer, BigDecimal> accountMapForDebit = new LinkedHashMap<>();
BigDecimal totalDebitAmount = new BigDecimal(0); BigDecimal totalDebitAmount = new BigDecimal(0);
@@ -723,37 +699,37 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (isMarkedFraud) { if (isMarkedFraud) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} }
} else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) { } else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) {
if (isMarkedFraud) { if (isMarkedFraud) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} }
} else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isRepayment()) { } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} }
} }
@@ -764,26 +740,26 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) { if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) { } else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isRepayment()) { } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} }
} }
@@ -794,37 +770,33 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) { if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) { } else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isRepayment()) { } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} else { } else {
if (isIncomeFromFee) { if (isIncomeFromFee) {
this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode,
AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate,
feesAmount, isReversal, loanTransactionDTO.getFeePayments()); feesAmount, isReversal, loanTransactionDTO.getFeePayments());
Integer accountDebit = AccrualAccountsForLoan.FUND_SOURCE.getValue(); GLAccount debitAccount = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
if (accountMapForDebit.containsKey(accountDebit)) { AccrualAccountsForLoan.FUND_SOURCE.getValue(), paymentTypeId);
BigDecimal amount = accountMapForDebit.get(accountDebit).add(feesAmount); glAccountBalanceHolder.addToDebit(debitAccount, feesAmount);
accountMapForDebit.put(accountDebit, amount);
} else {
accountMapForDebit.put(accountDebit, feesAmount);
}
} else { } else {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} }
} }
@@ -837,32 +809,32 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) { if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) { } else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isRepayment()) { } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
if (isIncomeFromFee) { if (isIncomeFromFee) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
accountMapForCredit, accountMapForDebit); glAccountBalanceHolder);
} }
} }
@@ -873,27 +845,28 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
totalDebitAmount = totalDebitAmount.add(overPaymentAmount); totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) { if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) { } else if (loanTransactionDTO.getTransactionType().isPayoutRefund()) {
populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(), glAccountBalanceHolder);
} else { } else {
populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), accountMapForCredit, accountMapForDebit); AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
} }
} }
// create credit entries // create credit entries
for (Map.Entry<GLAccount, BigDecimal> creditEntry : accountMapForCredit.entrySet()) { for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), isReversal, creditEntry.getKey()); creditEntry.getValue(), isReversal, glAccount);
} }
if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) {
@@ -911,9 +884,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
transactionDate, totalDebitAmount, isReversal); transactionDate, totalDebitAmount, isReversal);
} else { } else {
// create debit entries // create debit entries
for (Map.Entry<Integer, BigDecimal> debitEntry : accountMapForDebit.entrySet()) { for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, debitEntry.getKey().intValue(), GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue(), isReversal); this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate,
debitEntry.getValue(), isReversal, glAccount);
} }
} }
} }