diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java index 4d8e854de..1f7e8b656 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java @@ -453,6 +453,30 @@ public class AccountingProcessorHelper { transactionId, transactionDate, amount); } + public void createSplitJournalEntriesAndReversalsForLoan(Office office, String currencyCode, + List splitAccountsHolder, JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, + Long loanId, String transactionId, LocalDate transactionDate, Boolean isReversal) { + splitAccountsHolder.forEach(journalItemHolder -> { + final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, journalItemHolder.getAccountType(), paymentTypeId); + if (isReversal) { + createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, + journalItemHolder.getAmount()); + } else { + createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, + journalItemHolder.getAmount()); + } + + }); + final GLAccount totalAccount = getLinkedGLAccountForLoanProduct(loanProductId, totalAccountHolder.getAccountType(), paymentTypeId); + if (isReversal) { + createDebitJournalEntryForLoan(office, currencyCode, totalAccount, loanId, transactionId, transactionDate, + totalAccountHolder.getAmount()); + } else { + createCreditJournalEntryForLoan(office, currencyCode, totalAccount, loanId, transactionId, transactionDate, + totalAccountHolder.getAmount()); + } + } + public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final CashAccountsForLoan accountMappingType, final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java index e75944f68..f99ed0df5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java @@ -129,7 +129,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess Map accountMap = new HashMap<>(); // handle principal payment (and reversals) - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId); @@ -137,7 +137,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle interest payment (and reversals) - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId); @@ -150,7 +150,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle fees payment (and reversals) - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId); @@ -163,7 +163,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle penalties payment (and reversals) - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId); @@ -176,7 +176,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle overpayment - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId); @@ -193,7 +193,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess entry.getValue(), isReversal, entry.getKey()); } - if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId(); Integer accountMappingTypeId; if (loanTransactionDTO.getLoanChargeData().isPenalty()) { @@ -327,7 +327,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess Map accountMap = new HashMap<>(); // handle principal payment or writeOff (and reversals) - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId); @@ -335,7 +335,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle interest payment of writeOff (and reversals) - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId); @@ -348,7 +348,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle fees payment of writeOff (and reversals) - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); @@ -369,7 +369,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } // handle penalties payment of writeOff (and reversals) - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); if (isIncomeFromFee) { GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -392,7 +392,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId); @@ -412,7 +412,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess /** * Single DEBIT transaction for write-offs or Repayments (and their reversals) ***/ - if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { if (writeOff) { this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId, @@ -444,13 +444,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to * those related to the repayment above ***/ - if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { - if (loanTransactionDTO.getTransactionType().isChargeRefund()) { - Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, - AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - totalDebitAmount, isReversal); - } + if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0 && loanTransactionDTO.getTransactionType().isChargeRefund()) { + Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); + this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, + AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, + totalDebitAmount, isReversal); } } @@ -510,19 +508,19 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); // create journal entries for recognizing interest (or reversal) - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversed); } // create journal entries for the fees application (or reversal) - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { this.helper.createJournalEntriesAndReversalsForLoanCharges(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount, isReversed, loanTransactionDTO.getFeePayments()); } // create journal entries for the penalties application (or reversal) - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { this.helper.createJournalEntriesAndReversalsForLoanCharges(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), @@ -565,13 +563,28 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess // transaction properties final String transactionId = loanTransactionDTO.getTransactionId(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); - final BigDecimal refundAmount = loanTransactionDTO.getAmount(); final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), - AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + BigDecimal overpaymentAmount = loanTransactionDTO.getOverPayment(); + BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); + + BigDecimal totalAmount = BigDecimal.ZERO; + List journalAmountHolders = new ArrayList<>(); + + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { + totalAmount = totalAmount.add(principalAmount); + journalAmountHolders.add(new JournalAmountHolder(AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), principalAmount)); + } + if (overpaymentAmount != null && overpaymentAmount.compareTo(BigDecimal.ZERO) > 0) { + totalAmount = totalAmount.add(overpaymentAmount); + journalAmountHolders.add(new JournalAmountHolder(AccrualAccountsForLoan.OVERPAYMENT.getValue(), overpaymentAmount)); + } + + JournalAmountHolder totalAmountHolder = new JournalAmountHolder(AccrualAccountsForLoan.FUND_SOURCE.getValue(), totalAmount); + helper.createSplitJournalEntriesAndReversalsForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, isReversal); + } private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) { @@ -594,19 +607,19 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess BigDecimal totalDebitAmount = new BigDecimal(0); - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, !isReversal); } - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, !isReversal); } - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); List chargePaymentDTOs = new ArrayList<>(); @@ -622,7 +635,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess !isReversal, chargePaymentDTOs); } - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); List chargePaymentDTOs = new ArrayList<>(); @@ -638,7 +651,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess penaltiesAmount, !isReversal, chargePaymentDTOs); } - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, !isReversal); diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java index 2e9589496..e4f1aa592 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java @@ -92,7 +92,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF **/ else if (loanTransactionDTO.getTransactionType().isWriteOff()) { final BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, loanTransactionDTO.isReversed()); @@ -140,7 +140,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF Map accountMap = new HashMap<>(); // handle principal payment (and reversals) - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccountingConstants.CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId); @@ -148,7 +148,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } // handle interest payment (and reversals) - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccountingConstants.CashAccountsForLoan.INTEREST_ON_LOANS.getValue(), paymentTypeId); @@ -161,7 +161,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } // handle fees payment (and reversals) - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccountingConstants.CashAccountsForLoan.INCOME_FROM_FEES.getValue(), paymentTypeId); @@ -174,7 +174,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } // handle penalties payment (and reversals) - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), paymentTypeId); @@ -187,7 +187,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } // handle overpayment - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccountingConstants.CashAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId); @@ -204,7 +204,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF entry.getValue(), isReversal, entry.getKey()); } - if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId(); Integer accountMappingTypeId; if (loanTransactionDTO.getLoanChargeData().isPenalty()) { @@ -342,33 +342,33 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF BigDecimal totalDebitAmount = new BigDecimal(0); - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, isReversal); } - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversal); } - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount, isReversal, loanTransactionDTO.getFeePayments()); } - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, isReversal, loanTransactionDTO.getPenaltyPayments()); } - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, isReversal); @@ -396,13 +396,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to * those related to the repayment above ***/ - if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { - if (loanTransactionDTO.getTransactionType().isChargeRefund()) { - Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, - CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - totalDebitAmount, isReversal); - } + if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0 && loanTransactionDTO.getTransactionType().isChargeRefund()) { + Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); + this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, + CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, + totalDebitAmount, isReversal); } } @@ -477,13 +475,27 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF // transaction properties final String transactionId = loanTransactionDTO.getTransactionId(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); - final BigDecimal refundAmount = loanTransactionDTO.getAmount(); final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), - CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + BigDecimal overpaymentAmount = loanTransactionDTO.getOverPayment(); + BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); + + BigDecimal totalAmount = BigDecimal.ZERO; + List journalAmountHolders = new ArrayList<>(); + + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { + totalAmount = totalAmount.add(principalAmount); + journalAmountHolders.add(new JournalAmountHolder(CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), principalAmount)); + } + if (overpaymentAmount != null && overpaymentAmount.compareTo(BigDecimal.ZERO) > 0) { + totalAmount = totalAmount.add(overpaymentAmount); + journalAmountHolders.add(new JournalAmountHolder(CashAccountsForLoan.OVERPAYMENT.getValue(), overpaymentAmount)); + } + + JournalAmountHolder totalAmountHolder = new JournalAmountHolder(CashAccountsForLoan.FUND_SOURCE.getValue(), totalAmount); + helper.createSplitJournalEntriesAndReversalsForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, isReversal); } private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) { @@ -505,19 +517,19 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF BigDecimal totalDebitAmount = new BigDecimal(0); - if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, !isReversal); } - if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, !isReversal); } - if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); List chargePaymentDTOs = new ArrayList<>(); @@ -533,7 +545,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF !isReversal, chargePaymentDTOs); } - if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); List chargePaymentDTOs = new ArrayList<>(); @@ -549,7 +561,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF penaltiesAmount, !isReversal, chargePaymentDTOs); } - if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(overPaymentAmount); this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, !isReversal); diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalAmountHolder.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalAmountHolder.java new file mode 100644 index 000000000..2ec625bbe --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalAmountHolder.java @@ -0,0 +1,33 @@ +/** + * 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.service; + +import java.math.BigDecimal; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class JournalAmountHolder { + + private Integer accountType; + private BigDecimal amount; +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java index 79adfb007..3aa41f676 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java @@ -53,6 +53,7 @@ import java.util.Set; import java.util.UUID; import org.apache.fineract.accounting.glaccount.domain.GLAccountType; import org.apache.fineract.client.models.BusinessDateRequest; +import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse; import org.apache.fineract.client.models.GetLoanTransactionRelation; import org.apache.fineract.client.models.GetLoansLoanIdLoanTransactionRelation; import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod; @@ -60,6 +61,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdSummary; import org.apache.fineract.client.models.GetLoansLoanIdTransactions; import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; +import org.apache.fineract.client.models.JournalEntryTransactionItem; import org.apache.fineract.client.models.PostChargesRequest; import org.apache.fineract.client.models.PostChargesResponse; import org.apache.fineract.client.models.PostClientsResponse; @@ -7197,6 +7199,18 @@ public class ClientLoanIntegrationTest { assertEquals(LocalDate.of(2022, 10, 10), loanDetails.getTransactions().get(3).getDate()); assertEquals(0.0, loanDetails.getTransactions().get(3).getOutstandingLoanBalance()); + GetJournalEntriesTransactionIdResponse journalEntriesForTransaction = journalEntryHelper + .getJournalEntries("L" + loanDetails.getTransactions().get(3).getId()); + List journalItems = journalEntriesForTransaction.getPageItems(); + assertEquals(2, journalItems.size()); + assertEquals(200.0, + journalItems.stream() + .filter(j -> "DEBIT".equalsIgnoreCase(j.getEntryType().getValue()) + && j.getGlAccountId().equals(overpaymentAccount.getAccountID().longValue())) + .findFirst().get().getAmount()); + assertEquals(200.0, journalItems.stream().filter(j -> "CREDIT".equalsIgnoreCase(j.getEntryType().getValue()) + && j.getGlAccountId().equals(assetAccount.getAccountID().longValue())).findFirst().get().getAmount()); + this.loanTransactionHelper.reverseLoanTransaction(loanDetails.getId(), loanDetails.getTransactions().get(1).getId(), new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy").transactionAmount(0.0) .transactionDate("10 October 2022").locale("en")); @@ -7232,6 +7246,25 @@ public class ClientLoanIntegrationTest { assertEquals(100.0, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPrincipalPaid()); assertEquals(100.0, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPrincipalOutstanding()); + journalEntriesForTransaction = journalEntryHelper.getJournalEntries("L" + loanDetails.getTransactions().get(3).getId()); + journalItems = journalEntriesForTransaction.getPageItems(); + assertEquals(3, journalItems.size()); + assertEquals(1, + journalItems.stream().filter(item -> item.getAmount() == 200.0d) + .filter(j -> "CREDIT".equalsIgnoreCase(j.getEntryType().getValue()) + && j.getGlAccountId().equals(assetAccount.getAccountID().longValue())) + .count()); + assertEquals(1, + journalItems.stream().filter(item -> item.getAmount() == 100.0d) + .filter(j -> "DEBIT".equalsIgnoreCase(j.getEntryType().getValue()) + && j.getGlAccountId().equals(overpaymentAccount.getAccountID().longValue())) + .count()); + assertEquals(1, + journalItems.stream().filter(item -> item.getAmount() == 100.0d) + .filter(j -> "DEBIT".equalsIgnoreCase(j.getEntryType().getValue()) + && j.getGlAccountId().equals(assetAccount.getAccountID().longValue())) + .count()); + } finally { GlobalConfigurationHelper.updateIsAutomaticExternalIdGenerationEnabled(this.requestSpec, this.responseSpec, false); GlobalConfigurationHelper.updateIsBusinessDateEnabled(this.requestSpec, this.responseSpec, false);