FINERACT-1706: Accounting for Chargeback

This commit is contained in:
Adam Saghy
2022-08-23 10:02:14 +02:00
committed by Arnold Gálovics
parent b8ebc3967e
commit 23f672adbc
6 changed files with 149 additions and 165 deletions
@@ -321,39 +321,6 @@ public class AccountingProcessorHelper {
} }
/**
* Convenience method that creates a pair of related Debits and Credits for Accrual Based accounting.
*
* The target accounts for debits and credits are switched in case of a reversal
*
* @param office
* @param accountTypeToBeDebited
* Enum of the placeholder GLAccount to be debited
* @param accountTypeToBeCredited
* Enum of the placeholder of the GLAccount to be credited
* @param loanProductId
* @param paymentTypeId
* @param loanId
* @param transactionId
* @param transactionDate
* @param amount
* @param isReversal
*/
public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long loanId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount,
final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited;
int accountTypeToCreditId = accountTypeToBeCredited;
// reverse debits and credits for reversals
if (isReversal) {
accountTypeToDebitId = accountTypeToBeCredited;
accountTypeToCreditId = accountTypeToBeDebited;
}
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, amount);
}
/** /**
* Convenience method that creates a pair of related Debits and Credits for Accrual Based accounting. * Convenience method that creates a pair of related Debits and Credits for Accrual Based accounting.
* *
@@ -382,7 +349,7 @@ public class AccountingProcessorHelper {
* @param chargePaymentDTOs * @param chargePaymentDTOs
* chargePaymentDTOs * chargePaymentDTOs
*/ */
public void createAccrualBasedJournalEntriesAndReversalsForLoanCharges(final Office office, final String currencyCode, public void createJournalEntriesAndReversalsForLoanCharges(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long loanId, final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long loanId,
final String transactionId, final LocalDate transactionDate, final BigDecimal totalAmount, final Boolean isReversal, final String transactionId, final LocalDate transactionDate, final BigDecimal totalAmount, final Boolean isReversal,
final List<ChargePaymentDTO> chargePaymentDTOs) { final List<ChargePaymentDTO> chargePaymentDTOs) {
@@ -476,7 +443,7 @@ public class AccountingProcessorHelper {
* @param amount * @param amount
* @param isReversal * @param isReversal
*/ */
public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode, public void createJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId, final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
final Long loanId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Long loanId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount,
final Boolean isReversal) { final Boolean isReversal) {
@@ -27,7 +27,6 @@ import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.domain.GLClosure; import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan; import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
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;
@@ -96,9 +95,38 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) { else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) {
createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office); createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office);
} }
// Logic for Chargebacks
else if (loanTransactionDTO.getTransactionType().isChargeback()) {
createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office);
}
} }
} }
/**
* Handle chargeback journal entry creation
*
* @param loanDTO
* @param loanTransactionDTO
* @param office
*/
private void createJournalEntriesForChargeback(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
final String currencyCode = loanDTO.getCurrencyCode();
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal amount = loanTransactionDTO.getAmount();
final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, amount,
isReversal);
}
/** /**
* Debit loan Portfolio and credit Fund source for Disbursement. * Debit loan Portfolio and credit Fund source for Disbursement.
* *
@@ -124,17 +152,17 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// create journal entries for the disbursement (or disbursement // create journal entries for the disbursement (or disbursement
// reversal) // reversal)
if (loanTransactionDTO.isLoanToLoanTransfer()) { if (loanTransactionDTO.isLoanToLoanTransfer()) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed); disbursalAmount, isReversed);
} else if (loanTransactionDTO.isAccountTransfer()) { } else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed); disbursalAmount, isReversed);
} else { } else {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed); disbursalAmount, isReversed);
} }
} }
@@ -315,7 +343,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (loanTransactionDTO.getTransactionType().isChargeRefund()) { if (loanTransactionDTO.getTransactionType().isChargeRefund()) {
Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount,
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
totalDebitAmount, isReversal); totalDebitAmount, isReversal);
} }
@@ -341,7 +369,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount, isReversal); transactionDate, amount, isReversal);
@@ -379,20 +407,20 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// create journal entries for recognizing interest (or reversal) // 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.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversed); transactionDate, interestAmount, isReversed);
} }
// create journal entries for the fees application (or reversal) // 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.createAccrualBasedJournalEntriesAndReversalsForLoanCharges(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoanCharges(office, currencyCode,
AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId,
loanId, transactionId, transactionDate, feesAmount, isReversed, loanTransactionDTO.getFeePayments()); loanId, transactionId, transactionDate, feesAmount, isReversed, loanTransactionDTO.getFeePayments());
} }
// create journal entries for the penalties application (or reversal) // 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.createAccrualBasedJournalEntriesAndReversalsForLoanCharges(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoanCharges(office, currencyCode,
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(),
loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, isReversed, loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, isReversed,
loanTransactionDTO.getPenaltyPayments()); loanTransactionDTO.getPenaltyPayments());
@@ -413,12 +441,12 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.isAccountTransfer()) { if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount, isReversal); refundAmount, isReversal);
} else { } else {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount, isReversal); refundAmount, isReversal);
} }
} }
@@ -437,9 +465,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, refundAmount, isReversal); refundAmount, isReversal);
} }
private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) { private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
@@ -464,13 +492,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount); totalDebitAmount = totalDebitAmount.add(principalAmount);
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO, loanProductId, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO,
paymentTypeId, loanId, transactionId, transactionDate, principalAmount, !isReversal); 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); totalDebitAmount = totalDebitAmount.add(interestAmount);
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS,
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, !isReversal); loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount, !isReversal);
} }
@@ -486,7 +514,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
chargePaymentDTO.getLoanChargeId())); chargePaymentDTO.getLoanChargeId()));
} }
this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode,
CashAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount,
!isReversal, chargePaymentDTOs); !isReversal, chargePaymentDTOs);
} }
@@ -502,18 +530,18 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
} }
this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode,
CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate, AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate,
penaltiesAmount, !isReversal, chargePaymentDTOs); penaltiesAmount, !isReversal, chargePaymentDTOs);
} }
if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount); totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT, loanProductId, this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, !isReversal); paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, !isReversal);
} }
/*** create a single debit entry (or reversal) for the entire amount **/ /*** create a single debit entry (or reversal) for the entire amount **/
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, !isReversal); loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, !isReversal);
} }
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.closure.domain.GLClosure; import org.apache.fineract.accounting.closure.domain.GLClosure;
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan; import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity; import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO; import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
@@ -91,7 +90,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
else if (loanTransactionDTO.getTransactionType().isWriteOff()) { else if (loanTransactionDTO.getTransactionType().isWriteOff()) {
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) { if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode,
CashAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, CashAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, principalAmount, loanTransactionDTO.isReversed()); paymentTypeId, loanId, transactionId, transactionDate, principalAmount, loanTransactionDTO.isReversed());
@@ -105,9 +104,38 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) { else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) {
createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office); createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office);
} }
// Logic for Chargebacks
else if (loanTransactionDTO.getTransactionType().isChargeback()) {
createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office);
}
} }
} }
/**
* Handle chargeback journal entry creation
*
* @param loanDTO
* @param loanTransactionDTO
* @param office
*/
private void createJournalEntriesForChargeback(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
final String currencyCode = loanDTO.getCurrencyCode();
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal amount = loanTransactionDTO.getAmount();
final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, amount,
isReversal);
}
/** /**
* Debit loan Portfolio and credit Fund source for a Disbursement <br/> * Debit loan Portfolio and credit Fund source for a Disbursement <br/>
* *
@@ -132,17 +160,17 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.isLoanToLoanTransfer()) { if (loanTransactionDTO.isLoanToLoanTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversal); disbursalAmount, isReversal);
} else if (loanTransactionDTO.isAccountTransfer()) { } else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversal); disbursalAmount, isReversal);
} else { } else {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
loanId, transactionId, transactionDate, disbursalAmount, isReversal); disbursalAmount, isReversal);
} }
} }
@@ -171,11 +199,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.isAccountTransfer()) { if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(),
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount, isReversal); refundAmount, isReversal);
} else { } else {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(),
CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount, isReversal); refundAmount, isReversal);
} }
@@ -265,8 +293,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) { if (!(totalDebitAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (loanTransactionDTO.getTransactionType().isChargeRefund()) { if (loanTransactionDTO.getTransactionType().isChargeRefund()) {
Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType()); Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, incomeAccount,
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
totalDebitAmount, isReversal); totalDebitAmount, isReversal);
} }
} }
@@ -291,7 +319,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(),
CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
amount, isReversal); amount, isReversal);
@@ -322,14 +350,14 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
// final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); // final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.getTransactionType().isInitiateTransfer()) { if (loanTransactionDTO.getTransactionType().isInitiateTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(),
CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, null, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, null, loanId, transactionId, transactionDate,
loanId, transactionId, transactionDate, principalAmount, isReversal); principalAmount, isReversal);
} else if (loanTransactionDTO.getTransactionType().isApproveTransfer() } else if (loanTransactionDTO.getTransactionType().isApproveTransfer()
|| loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), loanProductId, null, CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), loanProductId, null, loanId, transactionId, transactionDate,
loanId, transactionId, transactionDate, principalAmount, isReversal); principalAmount, isReversal);
} }
} }
@@ -347,7 +375,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
CashAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, CashAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount, isReversal); refundAmount, isReversal);
} }
@@ -50,6 +50,8 @@ public class LoanTransactionEnumData {
private final boolean refundForActiveLoans; private final boolean refundForActiveLoans;
private final boolean creditBalanceRefund; private final boolean creditBalanceRefund;
private final boolean chargeback;
public LoanTransactionEnumData(final Long id, final String code, final String value) { public LoanTransactionEnumData(final Long id, final String code, final String value) {
this.id = id; this.id = id;
this.code = code; this.code = code;
@@ -75,6 +77,7 @@ public class LoanTransactionEnumData {
this.chargePayment = Long.valueOf(17).equals(this.id); this.chargePayment = Long.valueOf(17).equals(this.id);
this.refundForActiveLoans = Long.valueOf(18).equals(this.id); this.refundForActiveLoans = Long.valueOf(18).equals(this.id);
this.creditBalanceRefund = Long.valueOf(20).equals(this.id); this.creditBalanceRefund = Long.valueOf(20).equals(this.id);
this.chargeback = Long.valueOf(25).equals(this.id);
} }
public Long id() { public Long id() {
@@ -189,4 +192,8 @@ public class LoanTransactionEnumData {
return this.creditBalanceRefund; return this.creditBalanceRefund;
} }
public boolean isChargeback() {
return this.chargeback;
}
} }
@@ -56,7 +56,8 @@ public enum LoanTransactionType {
MERCHANT_ISSUED_REFUND(21, "loanTransactionType.merchantIssuedRefund"), // MERCHANT_ISSUED_REFUND(21, "loanTransactionType.merchantIssuedRefund"), //
PAYOUT_REFUND(22, "loanTransactionType.payoutRefund"), // PAYOUT_REFUND(22, "loanTransactionType.payoutRefund"), //
GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"), // GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit"), //
CHARGE_REFUND(24, "loanTransactionType.chargeRefund"); CHARGE_REFUND(24, "loanTransactionType.chargeRefund"), //
CHARGEBACK(25, "loanTransactionType.chargeback");
private final Integer value; private final Integer value;
private final String code; private final String code;
@@ -80,82 +81,33 @@ public enum LoanTransactionType {
return LoanTransactionType.INVALID; return LoanTransactionType.INVALID;
} }
LoanTransactionType loanTransactionType = null; return switch (transactionType) {
switch (transactionType) { case 1 -> LoanTransactionType.DISBURSEMENT;
case 1: case 2 -> LoanTransactionType.REPAYMENT;
loanTransactionType = LoanTransactionType.DISBURSEMENT; case 3 -> LoanTransactionType.CONTRA;
break; case 4 -> LoanTransactionType.WAIVE_INTEREST;
case 2: case 5 -> LoanTransactionType.REPAYMENT_AT_DISBURSEMENT;
loanTransactionType = LoanTransactionType.REPAYMENT; case 6 -> LoanTransactionType.WRITEOFF;
break; case 7 -> LoanTransactionType.MARKED_FOR_RESCHEDULING;
case 3: case 8 -> LoanTransactionType.RECOVERY_REPAYMENT;
loanTransactionType = LoanTransactionType.CONTRA; case 9 -> LoanTransactionType.WAIVE_CHARGES;
break; case 10 -> LoanTransactionType.ACCRUAL;
case 4: case 12 -> LoanTransactionType.INITIATE_TRANSFER;
loanTransactionType = LoanTransactionType.WAIVE_INTEREST; case 13 -> LoanTransactionType.APPROVE_TRANSFER;
break; case 14 -> LoanTransactionType.WITHDRAW_TRANSFER;
case 5: case 15 -> LoanTransactionType.REJECT_TRANSFER;
loanTransactionType = LoanTransactionType.REPAYMENT_AT_DISBURSEMENT; case 16 -> LoanTransactionType.REFUND;
break; case 17 -> LoanTransactionType.CHARGE_PAYMENT;
case 6: case 18 -> LoanTransactionType.REFUND_FOR_ACTIVE_LOAN;
loanTransactionType = LoanTransactionType.WRITEOFF; case 19 -> LoanTransactionType.INCOME_POSTING;
break; case 20 -> LoanTransactionType.CREDIT_BALANCE_REFUND;
case 7: case 21 -> LoanTransactionType.MERCHANT_ISSUED_REFUND;
loanTransactionType = LoanTransactionType.MARKED_FOR_RESCHEDULING; case 22 -> LoanTransactionType.PAYOUT_REFUND;
break; case 23 -> LoanTransactionType.GOODWILL_CREDIT;
case 8: case 24 -> LoanTransactionType.CHARGE_REFUND;
loanTransactionType = LoanTransactionType.RECOVERY_REPAYMENT; case 25 -> LoanTransactionType.CHARGEBACK;
break; default -> LoanTransactionType.INVALID;
case 9: };
loanTransactionType = LoanTransactionType.WAIVE_CHARGES;
break;
case 10:
loanTransactionType = LoanTransactionType.ACCRUAL;
break;
case 12:
loanTransactionType = LoanTransactionType.INITIATE_TRANSFER;
break;
case 13:
loanTransactionType = LoanTransactionType.APPROVE_TRANSFER;
break;
case 14:
loanTransactionType = LoanTransactionType.WITHDRAW_TRANSFER;
break;
case 15:
loanTransactionType = LoanTransactionType.REJECT_TRANSFER;
break;
case 16:
loanTransactionType = LoanTransactionType.REFUND;
break;
case 17:
loanTransactionType = LoanTransactionType.CHARGE_PAYMENT;
break;
case 18:
loanTransactionType = LoanTransactionType.REFUND_FOR_ACTIVE_LOAN;
break;
case 19:
loanTransactionType = LoanTransactionType.INCOME_POSTING;
break;
case 20:
loanTransactionType = LoanTransactionType.CREDIT_BALANCE_REFUND;
break;
case 21:
loanTransactionType = LoanTransactionType.MERCHANT_ISSUED_REFUND;
break;
case 22:
loanTransactionType = LoanTransactionType.PAYOUT_REFUND;
break;
case 23:
loanTransactionType = LoanTransactionType.GOODWILL_CREDIT;
break;
case 24:
loanTransactionType = LoanTransactionType.CHARGE_REFUND;
break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
}
return loanTransactionType;
} }
public boolean isDisbursement() { public boolean isDisbursement() {
@@ -287,6 +287,8 @@ public final class LoanEnumerations {
LoanTransactionType.GOODWILL_CREDIT.getCode(), "Goodwill Credit"); LoanTransactionType.GOODWILL_CREDIT.getCode(), "Goodwill Credit");
case CHARGE_REFUND -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_REFUND.getValue().longValue(), case CHARGE_REFUND -> new LoanTransactionEnumData(LoanTransactionType.CHARGE_REFUND.getValue().longValue(),
LoanTransactionType.CHARGE_REFUND.getCode(), "Charge Refund"); LoanTransactionType.CHARGE_REFUND.getCode(), "Charge Refund");
case CHARGEBACK -> new LoanTransactionEnumData(LoanTransactionType.CHARGEBACK.getValue().longValue(),
LoanTransactionType.CHARGEBACK.getCode(), "Chargeback");
}; };
} }