From 957666df1838f0918215223f5810d42fddb7d6f8 Mon Sep 17 00:00:00 2001 From: mariiaKraievska Date: Wed, 26 Mar 2025 10:30:30 +0200 Subject: [PATCH] FINERACT-2210: Incorrect accounting of reversed charge-off transaction. --- .../journalentry/domain/JournalEntry.java | 10 +- .../data/loanproduct/DefaultLoanProduct.java | 1 + .../factory/LoanProductsRequestFactory.java | 122 +++++++ .../LoanProductGlobalInitializerStep.java | 28 ++ .../fineract/test/support/TestContextKey.java | 1 + .../resources/features/LoanChargeOff.feature | 44 ++- .../service/AccountingProcessorHelper.java | 248 +++++--------- ...ccrualBasedAccountingProcessorForLoan.java | 305 +++++++++--------- .../CashBasedAccountingProcessorForLoan.java | 267 +++++++-------- .../JournalEntryWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 22 ++ ...ateJournalEntriesForChargeOffLoanTest.java | 9 +- 12 files changed, 578 insertions(+), 481 deletions(-) diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java index ec86e22b5..d190e9f18 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntry.java @@ -27,6 +27,7 @@ import jakarta.persistence.Table; import java.math.BigDecimal; import java.time.LocalDate; import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; @@ -54,6 +55,7 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "currency_code", length = 3, nullable = false) private String currencyCode; + @Setter @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "reversal_id") private JournalEntry reversalJournalEntry; @@ -73,6 +75,7 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "share_transaction_id") private Long shareTransactionId; + @Setter @Column(name = "reversed", nullable = false) private boolean reversed = false; @@ -151,11 +154,4 @@ public class JournalEntry extends AbstractAuditableWithUTCDateTimeCustom { return JournalEntryType.CREDIT.getValue().equals(this.type); } - public void setReversalJournalEntry(final JournalEntry reversalJournalEntry) { - this.reversalJournalEntry = reversalJournalEntry; - } - - public void setReversed(final boolean reversed) { - this.reversed = reversed; - } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java index b8dde51b8..d3a6309d8 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java @@ -105,6 +105,7 @@ public enum DefaultLoanProduct implements LoanProduct { LP2_INTEREST_RECALCULATION_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR_LAST_INSTALLMENT_STRATEGY, // LP2_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR_LAST_INSTALLMENT_STRATEGY, // LP2_ADV_PYMNT_INTEREST_DAILY_INT_RECALCULATION_ZERO_INT_CHARGE_OFF_INT_RECOGNITION_FROM_DISB_DATE, // + LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON, // ; @Override diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java index 801777b59..d7df06807 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanProductsRequestFactory.java @@ -1250,4 +1250,126 @@ public class LoanProductsRequestFactory { .chargeOffFraudExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT_FRAUD))// .incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));// } + + public PostLoanProductsRequest defaultLoanProductsRequestLP2ChargeOffReasonToExpenseAccountMappings() { + final String name = Utils.randomNameGenerator(NAME_PREFIX_LP2, 4); + final String shortName = Utils.randomNameGenerator(SHORT_NAME_PREFIX, 3); + + final List principalVariationsForBorrowerCycle = new ArrayList<>(); + final List numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<>(); + final List interestRateVariationsForBorrowerCycle = new ArrayList<>(); + final List charges = new ArrayList<>(); + final List penaltyToIncomeAccountMappings = new ArrayList<>(); + final List feeToIncomeAccountMappings = new ArrayList<>(); + + final List paymentChannelToFundSourceMappings = new ArrayList<>(); + final GetLoanPaymentChannelToFundSourceMappings loanPaymentChannelToFundSourceMappings = new GetLoanPaymentChannelToFundSourceMappings(); + loanPaymentChannelToFundSourceMappings.fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES)); + loanPaymentChannelToFundSourceMappings.paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER)); + paymentChannelToFundSourceMappings.add(loanPaymentChannelToFundSourceMappings); + + final Long chargeOffReasonId = codeHelper.retrieveCodeByName(CHARGE_OFF_REASONS).getId(); + + final List chargeOffReasonToExpenseAccountMappings = new ArrayList<>(); + final PostChargeOffReasonToExpenseAccountMappings chargeOffDelinquentReason = new PostChargeOffReasonToExpenseAccountMappings(); + chargeOffDelinquentReason.chargeOffReasonCodeValueId(codeValueResolver.resolve(chargeOffReasonId, DefaultCodeValue.DELINQUENT)); + chargeOffDelinquentReason.expenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT_FRAUD)); + chargeOffReasonToExpenseAccountMappings.add(chargeOffDelinquentReason); + + return new PostLoanProductsRequest()// + .name(name)// + .shortName(shortName)// + .description(DESCRIPTION_LP2)// + .enableDownPayment(true)// + .enableAutoRepaymentForDownPayment(true)// + .disbursedAmountPercentageForDownPayment(new BigDecimal(25))// + .fundId(FUND_ID)// + .startDate(null)// + .closeDate(null)// + .includeInBorrowerCycle(false)// + .currencyCode(CURRENCY_CODE)// + .digitsAfterDecimal(2)// + .inMultiplesOf(0)// + .installmentAmountInMultiplesOf(1)// + .useBorrowerCycle(false)// + .minPrincipal(100.0)// + .principal(1000.0)// + .maxPrincipal(10000.0)// + .minNumberOfRepayments(1)// + .numberOfRepayments(3)// + .maxNumberOfRepayments(30)// + .isLinkedToFloatingInterestRates(false)// + .minInterestRatePerPeriod((double) 0)// + .interestRatePerPeriod((double) 0)// + .maxInterestRatePerPeriod((double) 0)// + .interestRateFrequencyType(INTEREST_RATE_FREQUENCY_TYPE_MONTH)// + .repaymentEvery(15)// + .repaymentFrequencyType(REPAYMENT_FREQUENCY_TYPE_DAYS)// + .principalVariationsForBorrowerCycle(principalVariationsForBorrowerCycle)// + .numberOfRepaymentVariationsForBorrowerCycle(numberOfRepaymentVariationsForBorrowerCycle)// + .interestRateVariationsForBorrowerCycle(interestRateVariationsForBorrowerCycle)// + .amortizationType(AMORTIZATION_TYPE)// + .interestType(INTEREST_TYPE_DECLINING_BALANCE)// + .isEqualAmortization(false)// + .interestCalculationPeriodType(INTEREST_CALCULATION_PERIOD_TYPE_SAME_AS_REPAYMENT)// + .transactionProcessingStrategyCode(TRANSACTION_PROCESSING_STRATEGY_CODE)// + .daysInYearType(DAYS_IN_YEAR_TYPE)// + .daysInMonthType(DAYS_IN_MONTH_TYPE)// + .canDefineInstallmentAmount(true)// + .graceOnArrearsAgeing(3)// + .overdueDaysForNPA(179)// + .accountMovesOutOfNPAOnlyOnArrearsCompletion(false)// + .principalThresholdForLastInstallment(50)// + .allowVariableInstallments(false)// + .canUseForTopup(false)// + .isInterestRecalculationEnabled(false)// + .holdGuaranteeFunds(false)// + .multiDisburseLoan(true)// + .allowAttributeOverrides(new AllowAttributeOverrides()// + .amortizationType(true)// + .interestType(true)// + .transactionProcessingStrategyCode(true)// + .interestCalculationPeriodType(true)// + .inArrearsTolerance(true)// + .repaymentEvery(true)// + .graceOnPrincipalAndInterestPayment(true)// + .graceOnArrearsAgeing(true))// + .allowPartialPeriodInterestCalcualtion(true)// + .maxTrancheCount(10)// + .outstandingLoanBalance(10000.0)// + .charges(charges)// + .accountingRule(LOAN_ACCOUNTING_RULE)// + .fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.SUSPENSE_CLEARING_ACCOUNT))// + .loanPortfolioAccountId(accountTypeResolver.resolve(DefaultAccountType.LOANS_RECEIVABLE))// + .transfersInSuspenseAccountId(accountTypeResolver.resolve(DefaultAccountType.TRANSFER_IN_SUSPENSE_ACCOUNT))// + .interestOnLoanAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME))// + .incomeFromFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))// + .incomeFromPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))// + .incomeFromRecoveryAccountId(accountTypeResolver.resolve(DefaultAccountType.RECOVERIES))// + .writeOffAccountId(accountTypeResolver.resolve(DefaultAccountType.WRITTEN_OFF))// + .overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OVERPAYMENT_ACCOUNT))// + .receivableInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))// + .receivableFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))// + .receivablePenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))// + .dateFormat(DATE_FORMAT)// + .locale(LOCALE_EN)// + .disallowExpectedDisbursements(true)// + .allowApprovedDisbursedAmountsOverApplied(true)// + .overAppliedCalculationType(OVER_APPLIED_CALCULATION_TYPE)// + .overAppliedNumber(OVER_APPLIED_NUMBER)// + .delinquencyBucketId(DELINQUENCY_BUCKET_ID.longValue())// + .goodwillCreditAccountId(accountTypeResolver.resolve(DefaultAccountType.GOODWILL_EXPENSE_ACCOUNT))// + .incomeFromGoodwillCreditInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))// + .incomeFromGoodwillCreditFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))// + .incomeFromGoodwillCreditPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))// + .paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)// + .penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)// + .feeToIncomeAccountMappings(feeToIncomeAccountMappings)// + .incomeFromChargeOffInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))// + .incomeFromChargeOffFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))// + .chargeOffExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT))// + .chargeOffFraudExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT))// + .chargeOffReasonToExpenseAccountMappings(chargeOffReasonToExpenseAccountMappings)// + .incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));// + } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java index c7f3c2a40..c995b7975 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java @@ -1975,6 +1975,34 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ .createLoanProduct(loanProductsRequestInterest36030DecliningPeriodDailyIntRecalc).execute(); TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_360_30__DECLINING_PERIOD_DAILY_INT_RECALC, responseInterest36030DecliningPeriodDailyIntRecalc); + + // LP2 + zero-interest chargeOff behaviour + progressive loan schedule + horizontal + delinquent charge-off + // reason to GL account mapping + // (LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON) + final String name83 = DefaultLoanProduct.LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON.getName(); + + final PostLoanProductsRequest loanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReason = loanProductsRequestFactory + .defaultLoanProductsRequestLP2ChargeOffReasonToExpenseAccountMappings()// + .name(name83)// + .enableDownPayment(false)// + .enableAutoRepaymentForDownPayment(null)// + .disbursedAmountPercentageForDownPayment(null)// + .transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION.getValue())// + .loanScheduleType("PROGRESSIVE") // + .loanScheduleProcessingType("HORIZONTAL")// + .interestRateFrequencyType(3)// + .maxInterestRatePerPeriod(10.0)// + .paymentAllocation(List.of(// + createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), // + createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), // + createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), // + createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")))// + .chargeOffBehaviour("ZERO_INTEREST");// + final Response responseLoanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReason = loanProductsApi + .createLoanProduct(loanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReason).execute(); + TestContext.INSTANCE.set( + TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON, + responseLoanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReason); } public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule, diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 51b118d42..418d3be8d 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -116,6 +116,7 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationZeroInterestChargeOff"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentZeroInterestChargeOffBehaviour"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentZeroInterestChargeOff"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON = "loanProductCreateResponseLP2AdvancedPaymentZeroInterestChargeOffDelinquentReason"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_ALLOW_PARTIAL_PERIOD = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyAllowPartialPeriod"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_ACCRUAL_ACTIVITY_POSTING = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyAccrualActivityPosting"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_CHARGEBACK_INTEREST_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030ChargebackInterestFeePrincipal"; diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature index 9de439ff7..bbe446cd2 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature @@ -7541,4 +7541,46 @@ Feature: Charge-off | 20 January 2024 | Accrual Adjustment | 12.58 | 0.0 | 12.58 | 0.0 | 0.0 | 0.0 | false | false | | 20 January 2024 | Charge-off | 790.71 | 757.67 | 33.04 | 0.0 | 0.0 | 0.0 | false | false | Then Loan marked as charged-off on "20 January 2024" - Then LoanAccrualAdjustmentTransactionBusinessEvent is raised on "20 January 2024" \ No newline at end of file + Then LoanAccrualAdjustmentTransactionBusinessEvent is raised on "20 January 2024" + + @TestRailId:C3567 + Scenario: Charge-off reversal on a fraud loan respects GL mapping based on charge-off reason + When Admin sets the business date to "1 January 2024" + And Admin creates a client with random data + And Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | | | | 0.0 | + | 1 | 31 | 01 February 2024 | | 83.59 | 16.41 | 0.59 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 2 | 29 | 01 March 2024 | | 67.05 | 16.54 | 0.46 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 3 | 31 | 01 April 2024 | | 50.45 | 16.6 | 0.4 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 4 | 30 | 01 May 2024 | | 33.74 | 16.71 | 0.29 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 5 | 31 | 01 June 2024 | | 16.94 | 16.8 | 0.2 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 | + | 6 | 30 | 01 July 2024 | | 0.0 | 16.94 | 0.1 | 0.0 | 0.0 | 17.04 | 0.0 | 0.0 | 0.0 | 17.04 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 100 | 2.04 | 0 | 0 | 102.04 | 0 | 0 | 0 | 102.04 | + And Admin successfully approves the loan on "1 January 2024" with "100" amount and expected disbursement date on "1 January 2024" + And Admin successfully disburse the loan on "1 January 2024" with "100" EUR transaction amount + Then Admin can successfully set Fraud flag to the loan + When Admin sets the business date to "03 February 2024" + And Admin does charge-off the loan with reason "DELINQUENT" on "03 February 2024" + Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | | 100.0 | + | ASSET | 112603 | Interest/Fee Receivable | | 0.62 | + | EXPENSE | 744037 | Credit Loss/Bad Debt-Fraud | 100.0 | | + | INCOME | 404001 | Interest Income Charge Off | 0.62 | | + Then Admin does a charge-off undo the loan + Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "03 February 2024" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | | 100.0 | + | ASSET | 112601 | Loans Receivable | 100.0 | | + | ASSET | 112603 | Interest/Fee Receivable | | 0.62 | + | ASSET | 112603 | Interest/Fee Receivable | 0.62 | | + | EXPENSE | 744037 | Credit Loss/Bad Debt-Fraud | 100.0 | | + | EXPENSE | 744037 | Credit Loss/Bad Debt-Fraud | | 100.0 | + | INCOME | 404001 | Interest Income Charge Off | 0.62 | | + | INCOME | 404001 | Interest Income Charge Off | | 0.62 | 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 d9ee4a5fb..c000482be 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 @@ -68,14 +68,11 @@ import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; -import org.apache.fineract.portfolio.client.domain.ClientTransaction; import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData; import org.springframework.dao.DataAccessException; @@ -356,15 +353,12 @@ public class AccountingProcessorHelper { * transactionDate * @param totalAmount * totalAmount - * @param isReversal - * isReversal * @param chargePaymentDTOs * chargePaymentDTOs */ - public void createJournalEntriesAndReversalsForLoanCharges(final Office office, final String currencyCode, - 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 List chargePaymentDTOs) { + public void createJournalEntriesForLoanCharges(final Office office, final String currencyCode, final Integer accountTypeToBeDebited, + final Integer accountTypeToBeCredited, final Long loanProductId, final Long loanId, final String transactionId, + final LocalDate transactionDate, final BigDecimal totalAmount, final List chargePaymentDTOs) { GLAccount receivableAccount = getLinkedGLAccountForLoanCharges(loanProductId, accountTypeToBeDebited, null); final Map creditDetailsMap = new LinkedHashMap<>(); @@ -387,13 +381,8 @@ public class AccountingProcessorHelper { final GLAccount account = entry.getKey(); final BigDecimal amount = entry.getValue(); totalCreditedAmount = totalCreditedAmount.add(amount); - if (isReversal) { - createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - createCreditJournalEntryForLoan(office, currencyCode, receivableAccount, loanId, transactionId, transactionDate, amount); - } else { - createDebitJournalEntryForLoan(office, currencyCode, receivableAccount, loanId, transactionId, transactionDate, amount); - createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } + createDebitJournalEntryForLoan(office, currencyCode, receivableAccount, loanId, transactionId, transactionDate, amount); + createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); } if (totalAmount.compareTo(totalCreditedAmount) != 0) { @@ -453,61 +442,43 @@ public class AccountingProcessorHelper { * @param transactionId * @param transactionDate * @param amount - * @param isReversal */ - public void createJournalEntriesAndReversalsForLoan(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) { + public void createJournalEntriesForLoan(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) { 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); } - public void createSplitJournalEntriesAndReversalsForLoan(Office office, String currencyCode, - List splitAccountsHolder, JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, - Long loanId, String transactionId, LocalDate transactionDate, Boolean isReversal) { + public void createSplitJournalEntriesForLoan(Office office, String currencyCode, List splitAccountsHolder, + JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, Long loanId, String transactionId, + LocalDate transactionDate) { 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()); - } - + 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()); - } + createCreditJournalEntryForLoan(office, currencyCode, totalAccount, loanId, transactionId, transactionDate, + totalAccountHolder.getAmount()); } - public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, + public void createCreditJournalEntryForLoan(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) { + final String transactionId, final LocalDate transactionDate, final BigDecimal amount) { final int accountMappingTypeId = accountMappingType.getValue(); - createCreditJournalEntryOrReversalForLoan(office, currencyCode, accountMappingTypeId, loanProductId, paymentTypeId, loanId, - transactionId, transactionDate, amount, isReversal); + createCreditJournalEntryForLoan(office, currencyCode, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, amount); } - public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, + public void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final AccrualAccountsForLoan accountMappingType, final Long loanProductId, final Long paymentTypeId, final Long loanId, - final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { + final String transactionId, final LocalDate transactionDate, final BigDecimal amount) { final int accountMappingTypeId = accountMappingType.getValue(); - createCreditJournalEntryOrReversalForLoan(office, currencyCode, accountMappingTypeId, loanProductId, paymentTypeId, loanId, - transactionId, transactionDate, amount, isReversal); + createCreditJournalEntryForLoan(office, currencyCode, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, amount); } /** @@ -635,18 +606,6 @@ public class AccountingProcessorHelper { } } - public void createCashBasedDebitJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode, - final Long debitAccountId, final Long savingsId, final String transactionId, final LocalDate transactionDate, - final BigDecimal amount, final Boolean isReversal) { - // reverse debits and credits for reversals - final GLAccount debitAccount = getGLAccountById(debitAccountId); - if (isReversal) { - createCreditJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount); - } else { - createDebitJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount); - } - } - public void createCashBasedCreditJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode, final Long creditAccountId, final Long savingsId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { @@ -685,18 +644,6 @@ public class AccountingProcessorHelper { } } - public void createAccrualBasedDebitJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode, - final Long debitAccountId, final Long savingsId, final String transactionId, final LocalDate transactionDate, - final BigDecimal amount, final Boolean isReversal) { - // reverse debits and credits for reversals - final GLAccount debitAccount = getGLAccountById(debitAccountId); - if (isReversal) { - createCreditJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount); - } else { - createDebitJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount); - } - } - public void createAccrualBasedBasedCreditJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode, final Long creditAccountId, final Long savingsId, final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { @@ -723,80 +670,37 @@ public class AccountingProcessorHelper { createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount); } - public void createDebitJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final int accountMappingTypeId, + public void createDebitJournalEntryForLoan(final Office office, final String currencyCode, final int accountMappingTypeId, final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, - final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { + final LocalDate transactionDate, final BigDecimal amount) { final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId); - if (isReversal) { - createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } else { - createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } + createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); } - 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 createDebitJournalEntryForLoan(final Office office, final String currencyCode, final Long loanId, + final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final GLAccount account) { + createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); } - 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 LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { + public void createDebitJournalEntryForLoanCharges(final Office office, final String currencyCode, final int accountMappingTypeId, + final Long loanProductId, final Long chargeId, final Long loanId, final String transactionId, final LocalDate transactionDate, + final BigDecimal amount) { final GLAccount account = getLinkedGLAccountForLoanCharges(loanProductId, accountMappingTypeId, chargeId); - if (isReversal) { - createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } else { - createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } + createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); } - public void createCreditJournalEntryOrReversalForLoanCharges(final Office office, final String currencyCode, - final int accountMappingTypeId, final Long loanProductId, final Long loanId, final String transactionId, - final LocalDate transactionDate, final BigDecimal totalAmount, final Boolean isReversal, - final List chargePaymentDTOs) { - /*** - * Map to track each account and the net credit to be made for a particular account - ***/ - final Map creditDetailsMap = new LinkedHashMap<>(); - for (final ChargePaymentDTO chargePaymentDTO : chargePaymentDTOs) { - final Long chargeId = chargePaymentDTO.getChargeId(); - final GLAccount chargeSpecificAccount = getLinkedGLAccountForLoanCharges(loanProductId, accountMappingTypeId, chargeId); - BigDecimal chargeSpecificAmount = chargePaymentDTO.getAmount(); + public void createCreditJournalEntryForLoanCharges(final Office office, final String currencyCode, final int accountMappingTypeId, + final Long loanProductId, final Long loanId, final String transactionId, final LocalDate transactionDate, + final BigDecimal totalAmount, final List chargePaymentDTOs) { + createJournalEntriesForLoanChargesInternal(office, currencyCode, accountMappingTypeId, loanProductId, loanId, transactionId, + transactionDate, totalAmount, chargePaymentDTOs, true); + } - // adjust net credit amount if the account is already present in the - // map - if (creditDetailsMap.containsKey(chargeSpecificAccount)) { - final BigDecimal existingAmount = creditDetailsMap.get(chargeSpecificAccount); - chargeSpecificAmount = chargeSpecificAmount.add(existingAmount); - } - creditDetailsMap.put(chargeSpecificAccount, chargeSpecificAmount); - } - - BigDecimal totalCreditedAmount = BigDecimal.ZERO; - for (final Map.Entry entry : creditDetailsMap.entrySet()) { - final GLAccount account = entry.getKey(); - final BigDecimal amount = entry.getValue(); - totalCreditedAmount = totalCreditedAmount.add(amount); - if (isReversal) { - createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } else { - createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } - } - - // TODO: Vishwas Temporary validation to be removed before moving to - // release branch - if (totalAmount.compareTo(totalCreditedAmount) != 0) { - throw new PlatformDataIntegrityException( - "Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction", - "Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction", - totalCreditedAmount, totalAmount); - } + public void createDebitJournalEntryForLoanCharges(final Office office, final String currencyCode, final int accountMappingTypeId, + final Long loanProductId, final Long loanId, final String transactionId, final LocalDate transactionDate, + final BigDecimal totalAmount, final List chargePaymentDTOs) { + createJournalEntriesForLoanChargesInternal(office, currencyCode, accountMappingTypeId, loanProductId, loanId, transactionId, + transactionDate, totalAmount, chargePaymentDTOs, false); } /** @@ -899,38 +803,20 @@ public class AccountingProcessorHelper { } } - public LoanTransaction getLoanTransactionById(final Long loanTransactionId) { - return this.loanTransactionRepository.getReferenceById(loanTransactionId); - } - - public SavingsAccountTransaction getSavingsTransactionById(final Long savingsTransactionId) { - return this.savingsAccountTransactionRepository.getReferenceById(savingsTransactionId); - } - - public ClientTransaction getClientTransactionById(final Long clientTransactionId) { - return this.clientTransactionRepository.getReferenceById(clientTransactionId); - } - public Office getOfficeById(final long officeId) { return this.officeRepository.getReferenceById(officeId); } - public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final int accountMappingTypeId, + public void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final int accountMappingTypeId, final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, - final LocalDate transactionDate, final BigDecimal amount, final Boolean isReversal) { + final LocalDate transactionDate, final BigDecimal amount) { final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId); - createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, amount, isReversal, - account); + createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, amount, account); } - public void createCreditJournalEntryOrReversalForLoan(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) { - createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } else { - createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); - } + public void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final Long loanId, + final String transactionId, final LocalDate transactionDate, final BigDecimal amount, final GLAccount account) { + createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); } private void createCreditJournalEntryForClientPayments(final Office office, final String currencyCode, final GLAccount account, @@ -1403,4 +1289,38 @@ public class AccountingProcessorHelper { return savedJournalEntry; } + private void createJournalEntriesForLoanChargesInternal(final Office office, final String currencyCode, final int accountMappingTypeId, + final Long loanProductId, final Long loanId, final String transactionId, final LocalDate transactionDate, + final BigDecimal totalAmount, final List chargePaymentDTOs, final boolean isCredit) { + final Map creditDetailsMap = new LinkedHashMap<>(); + + for (final ChargePaymentDTO chargePaymentDTO : chargePaymentDTOs) { + final Long chargeId = chargePaymentDTO.getChargeId(); + final GLAccount account = getLinkedGLAccountForLoanCharges(loanProductId, accountMappingTypeId, chargeId); + BigDecimal amount = chargePaymentDTO.getAmount(); + + creditDetailsMap.merge(account, amount, BigDecimal::add); + } + + BigDecimal totalCreditedAmount = BigDecimal.ZERO; + + for (Map.Entry entry : creditDetailsMap.entrySet()) { + GLAccount account = entry.getKey(); + BigDecimal amount = entry.getValue(); + totalCreditedAmount = totalCreditedAmount.add(amount); + + if (isCredit) { + createCreditJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); + } else { + createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate, amount); + } + } + + if (totalAmount.compareTo(totalCreditedAmount) != 0) { + throw new PlatformDataIntegrityException( + "Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction", + "Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction", + totalCreditedAmount, totalAmount); + } + } } 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 999df7c37..852554001 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 @@ -46,16 +46,24 @@ import org.springframework.stereotype.Component; public class AccrualBasedAccountingProcessorForLoan implements AccountingProcessorForLoan { private final AccountingProcessorHelper helper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; @Override public void createJournalEntriesForLoan(final LoanDTO loanDTO) { - final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(loanDTO.getOfficeId()); - final Office office = this.helper.getOfficeById(loanDTO.getOfficeId()); + final Long officeId = loanDTO.getOfficeId(); + final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(officeId); + final Office office = this.helper.getOfficeById(officeId); for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) { final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); this.helper.checkForBranchClosures(latestGLClosure, transactionDate); final LoanTransactionEnumData transactionType = loanTransactionDTO.getTransactionType(); + if (loanTransactionDTO.isReversed()) { + journalEntryWritePlatformService.createJournalEntryForReversedLoanTransaction(transactionDate, + loanTransactionDTO.getTransactionId(), officeId); + continue; + } + // Handle Disbursements if (transactionType.isDisbursement()) { createJournalEntriesForDisbursements(loanDTO, loanTransactionDTO, office); @@ -67,8 +75,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } /* - * Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at - * disbursement (except charge adjustment) + * Handle repayments, loan refunds, repayments at disbursement (except charge adjustment) */ else if ((transactionType.isRepaymentType() && !transactionType.isChargeAdjustment()) || transactionType.isRepaymentAtDisbursement() || transactionType.isChargePayment()) { @@ -91,7 +98,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess createJournalEntriesForCreditBalanceRefund(loanDTO, loanTransactionDTO, office); } - // Handle Write Offs, waivers and their reversals + // Handle Write Offs else if ((transactionType.isWriteOff() || transactionType.isWaiveInterest() || transactionType.isWaiveCharges())) { createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, true, false); } @@ -135,7 +142,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPayment = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder(); if (isMarkedAsChargeOff) { @@ -202,14 +208,14 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess // create credit entries for (Map.Entry creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey()); - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - creditEntry.getValue(), isReversal, glAccount); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + creditEntry.getValue(), glAccount); } // create debit entries for (Map.Entry debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey()); - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - debitEntry.getValue(), isReversal, glAccount); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, debitEntry.getValue(), + glAccount); } } @@ -227,7 +233,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder(); // need to fetch if there are account mappings (always one) @@ -272,14 +277,14 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess // create credit entries for (Map.Entry creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey()); - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - creditEntry.getValue(), isReversal, glAccount); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + creditEntry.getValue(), glAccount); } // create debit entries for (Map.Entry debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey()); - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - debitEntry.getValue(), isReversal, glAccount); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, debitEntry.getValue(), + glAccount); } } @@ -318,7 +323,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); BigDecimal totalDebitAmount = new BigDecimal(0); @@ -386,8 +390,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } for (Map.Entry entry : accountMap.entrySet()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - entry.getValue(), isReversal, entry.getKey()); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(), + entry.getKey()); } if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -398,8 +402,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } else { accountMappingTypeId = AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(); } - this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, - loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, loanId, + transactionId, transactionDate, totalDebitAmount); } } @@ -418,13 +422,12 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); BigDecimal totalDebitAmount = new BigDecimal(0); Map accountMap = new LinkedHashMap<>(); - // handle principal payment (and reversals) + // handle principal payment if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -432,7 +435,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess accountMap.put(account, principalAmount); } - // handle interest payment (and reversals) + // handle interest payment if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -445,7 +448,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - // handle fees payment (and reversals) + // handle fees payment if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -458,7 +461,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - // handle penalties payment (and reversals) + // handle penalties payment if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -485,8 +488,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } for (Map.Entry entry : accountMap.entrySet()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - entry.getValue(), isReversal, entry.getKey()); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(), + entry.getKey()); } if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -497,8 +500,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } else { accountMappingTypeId = AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(); } - this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, - loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, loanId, + transactionId, transactionDate, totalDebitAmount); } } @@ -519,7 +522,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess 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(); final BigDecimal overpaidAmount = Objects.isNull(loanTransactionDTO.getOverPayment()) ? BigDecimal.ZERO : loanTransactionDTO.getOverPayment(); @@ -536,36 +538,36 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltyPaid = Objects.isNull(loanTransactionDTO.getPenaltyPaid()) ? BigDecimal.ZERO : loanTransactionDTO.getPenaltyPaid(); - helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId, - paymentTypeId, loanId, transactionId, transactionDate, amount, isReversal); + helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId, paymentTypeId, + loanId, transactionId, transactionDate, amount); if (overpaidAmount.compareTo(BigDecimal.ZERO) > 0) { - helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaidAmount, isReversal); + helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overpaidAmount); } if (principalCredited.compareTo(principalPaid) > 0) { - helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId, - paymentTypeId, loanId, transactionId, transactionDate, principalCredited.subtract(principalPaid), isReversal); + helper.createDebitJournalEntryForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, principalCredited.subtract(principalPaid)); } else if (principalCredited.compareTo(principalPaid) < 0) { - helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId, - paymentTypeId, loanId, transactionId, transactionDate, principalPaid.subtract(principalCredited), isReversal); + helper.createCreditJournalEntryForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, principalPaid.subtract(principalCredited)); } if (feeCredited.compareTo(feePaid) > 0) { - helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId, - loanId, transactionId, transactionDate, feeCredited.subtract(feePaid), isReversal); + helper.createDebitJournalEntryForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, feeCredited.subtract(feePaid)); } else if (feeCredited.compareTo(feePaid) < 0) { - helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId, - loanId, transactionId, transactionDate, feePaid.subtract(feeCredited), isReversal); + helper.createCreditJournalEntryForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, feePaid.subtract(feeCredited)); } if (penaltyCredited.compareTo(penaltyPaid) > 0) { - helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId, - loanId, transactionId, transactionDate, penaltyCredited.subtract(penaltyPaid), isReversal); + helper.createDebitJournalEntryForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, penaltyCredited.subtract(penaltyPaid)); } else if (penaltyCredited.compareTo(penaltyPaid) < 0) { - helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId, - loanId, transactionId, transactionDate, penaltyPaid.subtract(penaltyCredited), isReversal); + helper.createCreditJournalEntryForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId, loanId, + transactionId, transactionDate, penaltyPaid.subtract(penaltyCredited)); } } @@ -616,29 +618,27 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal overpaymentPortion = loanTransactionDTO.getOverPayment() != null ? loanTransactionDTO.getOverPayment() : BigDecimal.ZERO; final BigDecimal principalPortion = loanTransactionDTO.getAmount().subtract(overpaymentPortion); - final boolean isReversed = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - // create journal entries for the disbursement (or disbursement - // reversal) + // create journal entries for the disbursement if (MathUtil.isGreaterThanZero(principalPortion)) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion, isReversed); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion); } if (MathUtil.isGreaterThanZero(overpaymentPortion)) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion, isReversed); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion); } if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } else { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversed); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } } @@ -667,7 +667,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess * Penalty Write off: Debits "Losses Written off" and Credits "Receivable Penalties"
*
*
- * In case the loan transaction has been reversed, all debits are turned into credits and vice versa * * @param loanTransactionDTO * @param loanDTO @@ -701,7 +700,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder(); BigDecimal totalDebitAmount = new BigDecimal(0); @@ -801,9 +799,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } else { if (isIncomeFromFee) { - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, + this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, - feesAmount, isReversal, loanTransactionDTO.getFeePayments()); + feesAmount, loanTransactionDTO.getFeePayments()); GLAccount debitAccount = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.FUND_SOURCE.getValue(), paymentTypeId); glAccountBalanceHolder.addToDebit(debitAccount, feesAmount); @@ -879,44 +877,41 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess // create credit entries for (Map.Entry creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey()); - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - creditEntry.getValue(), isReversal, glAccount); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + creditEntry.getValue(), glAccount); } if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { if (writeOff) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { // create debit entries for (Map.Entry debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey()); - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - debitEntry.getValue(), isReversal, glAccount); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + debitEntry.getValue(), glAccount); } } } } /** - * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to - * those related to the repayment above + * Charge Refunds 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); + this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } @@ -938,14 +933,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); BigDecimal totalDebitAmount = new BigDecimal(0); Map accountMap = new LinkedHashMap<>(); Map debitAccountMapForGoodwillCredit = new LinkedHashMap<>(); - // handle principal payment or writeOff (and reversals) + // handle principal payment or writeOff if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -957,7 +951,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - // handle interest payment of writeOff (and reversals) + // handle interest payment of writeOff if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -975,15 +969,14 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - // handle fees payment of writeOff (and reversals) + // handle fees payment of writeOff if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); if (isIncomeFromFee) { - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, - AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, - feesAmount, isReversal, loanTransactionDTO.getFeePayments()); + this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), + loanProductId, loanId, transactionId, transactionDate, feesAmount, loanTransactionDTO.getFeePayments()); } else { GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId); @@ -1000,7 +993,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } } - // handle penalties payment of writeOff (and reversals) + // handle penalties payment of writeOff if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); if (isIncomeFromFee) { @@ -1047,53 +1040,48 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } for (Map.Entry entry : accountMap.entrySet()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - entry.getValue(), isReversal, entry.getKey()); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(), + entry.getKey()); } /** - * Single DEBIT transaction for write-offs or Repayments (and their reversals) + * Single DEBIT transaction for write-offs or Repayments ***/ if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { if (writeOff) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { // create debit entries for (Map.Entry debitEntry : debitAccountMapForGoodwillCredit.entrySet()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, debitEntry.getKey().intValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue(), - isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, debitEntry.getKey().intValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue()); } } else { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, - AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } } } /** - * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to - * those related to the repayment above + * Charge Refunds have an extra refund related pair of journal entries in addition to those related to the + * repayment above ***/ 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); + this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } @@ -1120,8 +1108,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess /** * Create a single Debit to fund source and a single credit to "Income from Recovery" - * - * In case the loan transaction is a reversal, all debits are turned into credits and vice versa */ private void createJournalEntriesForRecoveryRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) { @@ -1134,12 +1120,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess 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.FUND_SOURCE.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, - transactionDate, amount, isReversal); + transactionDate, amount); } @@ -1151,8 +1136,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess * * Penalties: Debit Penalties Receivable and credit Income from Penalties * - * Also handles reversals for both fees and payment applications - * * @param loanDTO * @param loanTransactionDTO * @param office @@ -1170,28 +1153,43 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal interestAmount = loanTransactionDTO.getInterest(); final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); - final boolean isReversed = transactionType.isAccrualAdjustment() != loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - // create journal entries for recognizing interest (or reversal) + // create journal entries for recognizing interest 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); + if (transactionType.isAccrualAdjustment()) { + this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), + AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, interestAmount); + } else { + this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), + AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, interestAmount); + } } - // create journal entries for the fees application (or reversal) + // create journal entries for the fees application 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()); + if (transactionType.isAccrualAdjustment()) { + this.helper.createJournalEntriesForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), + AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), loanProductId, loanId, transactionId, transactionDate, + feesAmount, loanTransactionDTO.getFeePayments()); + } else { + this.helper.createJournalEntriesForLoanCharges(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), + AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, + feesAmount, loanTransactionDTO.getFeePayments()); + } } - // create journal entries for the penalties application (or reversal) + // create journal entries for the penalties application if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { - - this.helper.createJournalEntriesAndReversalsForLoanCharges(office, currencyCode, - AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), - loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, isReversed, - loanTransactionDTO.getPenaltyPayments()); + if (transactionType.isAccrualAdjustment()) { + this.helper.createJournalEntriesForLoanCharges(office, currencyCode, + AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), + loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, loanTransactionDTO.getPenaltyPayments()); + } else { + this.helper.createJournalEntriesForLoanCharges(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), + AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate, + penaltiesAmount, loanTransactionDTO.getPenaltyPayments()); + } } } @@ -1205,17 +1203,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess 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(); if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + refundAmount); } else { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + refundAmount); } } @@ -1236,7 +1233,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess // transaction properties final String transactionId = loanTransactionDTO.getTransactionId(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); BigDecimal overpaymentAmount = loanTransactionDTO.getOverPayment(); @@ -1257,8 +1253,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess } JournalAmountHolder totalAmountHolder = new JournalAmountHolder(AccrualAccountsForLoan.FUND_SOURCE.getValue(), totalAmount); - helper.createSplitJournalEntriesAndReversalsForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, - paymentTypeId, loanId, transactionId, transactionDate, isReversal); + helper.createSplitJournalEntriesForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, paymentTypeId, + loanId, transactionId, transactionDate); } @@ -1293,21 +1289,20 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); BigDecimal totalDebitAmount = new BigDecimal(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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount); } 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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount); } if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -1321,9 +1316,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess : chargePaymentDTO.getAmount(), chargePaymentDTO.getLoanChargeId())); } - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, - AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount, - !isReversal, chargePaymentDTOs); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), + loanProductId, loanId, transactionId, transactionDate, feesAmount, chargePaymentDTOs); } if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -1336,21 +1330,18 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess : chargePaymentDTO.getAmount(), chargePaymentDTO.getLoanChargeId())); } - - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, - AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate, - penaltiesAmount, !isReversal, chargePaymentDTOs); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), + loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, chargePaymentDTOs); } 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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount); } /*** create a single debit entry (or reversal) for the entire amount **/ - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, !isReversal); - + this.helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } 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 770194ef7..6081ab9e0 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 @@ -35,6 +35,7 @@ import org.apache.fineract.accounting.journalentry.data.LoanDTO; import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.office.domain.Office; +import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData; import org.springframework.stereotype.Component; @Component @@ -42,48 +43,53 @@ import org.springframework.stereotype.Component; public class CashBasedAccountingProcessorForLoan implements AccountingProcessorForLoan { private final AccountingProcessorHelper helper; + private final JournalEntryWritePlatformService journalEntryWritePlatformService; @Override public void createJournalEntriesForLoan(final LoanDTO loanDTO) { - final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(loanDTO.getOfficeId()); + final Long officeId = loanDTO.getOfficeId(); + final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(officeId); final Long loanProductId = loanDTO.getLoanProductId(); final String currencyCode = loanDTO.getCurrencyCode(); + final Office office = this.helper.getOfficeById(officeId); for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) { final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); final String transactionId = loanTransactionDTO.getTransactionId(); - final Office office = this.helper.getOfficeById(loanTransactionDTO.getOfficeId()); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long loanId = loanDTO.getLoanId(); + final LoanTransactionEnumData transactionType = loanTransactionDTO.getTransactionType(); this.helper.checkForBranchClosures(latestGLClosure, transactionDate); - /** Handle Disbursements and reversals of disbursements **/ - if (loanTransactionDTO.getTransactionType().isDisbursement()) { + if (loanTransactionDTO.isReversed()) { + journalEntryWritePlatformService.createJournalEntryForReversedLoanTransaction(transactionDate, transactionId, officeId); + continue; + } + + /** Handle Disbursements **/ + if (transactionType.isDisbursement()) { createJournalEntriesForDisbursements(loanDTO, loanTransactionDTO, office); } /*** - * Logic for repayments, repayments at disbursement and reversal of Repayments and Repayments at - * disbursement (except charge adjustment) + * Logic for repayments, repayments at disbursement (except charge adjustment) ***/ - else if ((loanTransactionDTO.getTransactionType().isRepaymentType() - && !loanTransactionDTO.getTransactionType().isChargeAdjustment()) - || loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement() - || loanTransactionDTO.getTransactionType().isChargePayment()) { + else if ((transactionType.isRepaymentType() && !transactionType.isChargeAdjustment()) + || transactionType.isRepaymentAtDisbursement() || transactionType.isChargePayment()) { createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office); } /** Logic for handling recovery payments **/ - else if (loanTransactionDTO.getTransactionType().isRecoveryRepayment()) { + else if (transactionType.isRecoveryRepayment()) { createJournalEntriesForRecoveryRepayments(loanDTO, loanTransactionDTO, office); } /** Logic for Refunds of Overpayments **/ - else if (loanTransactionDTO.getTransactionType().isRefund()) { + else if (transactionType.isRefund()) { createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office); } /** Logic for Credit Balance Refunds **/ - else if (loanTransactionDTO.getTransactionType().isCreditBalanceRefund()) { + else if (transactionType.isCreditBalanceRefund()) { createJournalEntriesForCreditBalanceRefund(loanDTO, loanTransactionDTO, office); } @@ -91,33 +97,32 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF * Only principal write off affects cash based accounting (interest and fee write off need not be * considered). Debit losses written off and credit Loan Portfolio **/ - else if (loanTransactionDTO.getTransactionType().isWriteOff()) { + else if (transactionType.isWriteOff()) { final BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); 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()); + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(), + CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, paymentTypeId, loanId, transactionId, + transactionDate, principalAmount); } - } else if (loanTransactionDTO.getTransactionType().isInitiateTransfer() - || loanTransactionDTO.getTransactionType().isApproveTransfer() - || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { + } else if (transactionType.isInitiateTransfer() || transactionType.isApproveTransfer() + || transactionType.isWithdrawTransfer()) { createJournalEntriesForTransfers(loanDTO, loanTransactionDTO, office); } /** Logic for Refunds of Active Loans **/ - else if (loanTransactionDTO.getTransactionType().isRefundForActiveLoans()) { + else if (transactionType.isRefundForActiveLoans()) { createJournalEntriesForRefundForActiveLoan(loanDTO, loanTransactionDTO, office); } // Logic for Chargebacks - else if (loanTransactionDTO.getTransactionType().isChargeback()) { + else if (transactionType.isChargeback()) { createJournalEntriesForChargeback(loanDTO, loanTransactionDTO, office); } // Logic for Charge Adjustment - else if (loanTransactionDTO.getTransactionType().isChargeAdjustment()) { + else if (transactionType.isChargeAdjustment()) { createJournalEntriesForChargeAdjustment(loanDTO, loanTransactionDTO, office); } // Logic for Charge-Off - else if (loanTransactionDTO.getTransactionType().isChargeoff()) { + else if (transactionType.isChargeoff()) { createJournalEntriesForChargeOff(loanDTO, loanTransactionDTO, office); } } @@ -138,7 +143,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder(); @@ -174,14 +178,14 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF // create credit entries for (Map.Entry creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey()); - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - creditEntry.getValue(), isReversal, glAccount); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + creditEntry.getValue(), glAccount); } // create debit entries for (Map.Entry debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey()); - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - debitEntry.getValue(), isReversal, glAccount); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, debitEntry.getValue(), + glAccount); } } @@ -230,13 +234,12 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); BigDecimal totalDebitAmount = new BigDecimal(0); Map accountMap = new LinkedHashMap<>(); - // handle principal payment (and reversals) + // handle principal payment if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -244,7 +247,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF accountMap.put(account, principalAmount); } - // handle interest payment (and reversals) + // handle interest payment if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -257,7 +260,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } } - // handle fees payment (and reversals) + // handle fees payment if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -297,8 +300,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } for (Map.Entry entry : accountMap.entrySet()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - entry.getValue(), isReversal, entry.getKey()); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(), + entry.getKey()); } if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -309,8 +312,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } else { accountMappingTypeId = CashAccountsForLoan.INCOME_FROM_FEES.getValue(); } - this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, - loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, loanId, + transactionId, transactionDate, totalDebitAmount); } } @@ -329,13 +332,12 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); BigDecimal totalDebitAmount = new BigDecimal(0); Map accountMap = new LinkedHashMap<>(); - // handle principal payment (and reversals) + // handle principal payment if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(principalAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), @@ -343,7 +345,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF accountMap.put(account, principalAmount); } - // handle interest payment (and reversals) + // handle interest payment if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(interestAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -356,7 +358,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } } - // handle fees payment (and reversals) + // handle fees payment if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(feesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, CashAccountsForLoan.INCOME_FROM_FEES.getValue(), @@ -369,7 +371,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } } - // handle penalties payment (and reversals) + // handle penalties payment if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { totalDebitAmount = totalDebitAmount.add(penaltiesAmount); GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, @@ -396,8 +398,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } for (Map.Entry entry : accountMap.entrySet()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - entry.getValue(), isReversal, entry.getKey()); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(), + entry.getKey()); } if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -408,8 +410,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } else { accountMappingTypeId = CashAccountsForLoan.INCOME_FROM_FEES.getValue(); } - this.helper.createDebitJournalEntryOrReversalForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, - loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, accountMappingTypeId, loanProductId, chargeId, loanId, + transactionId, transactionDate, totalDebitAmount); } } @@ -430,20 +432,15 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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); + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), + CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, amount); } /** * Debit loan Portfolio and credit Fund source for a Disbursement
* - * All debits are turned into credits and vice versa in case of disbursement reversals - * - * * @param loanDTO * @param loanTransactionDTO * @param office @@ -461,36 +458,32 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal overpaymentPortion = loanTransactionDTO.getOverPayment() != null ? loanTransactionDTO.getOverPayment() : BigDecimal.ZERO; final BigDecimal principalPortion = loanTransactionDTO.getAmount().subtract(overpaymentPortion); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); if (MathUtil.isGreaterThanZero(principalPortion)) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPortion, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, principalPortion); } if (MathUtil.isGreaterThanZero(overpaymentPortion)) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion); } if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } else { - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount(), isReversal); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount()); } } /** * Debit loan Portfolio and credit Fund source for a Disbursement
* - * All debits are turned into credits and vice versa in case of disbursement reversals - * - * * @param loanDTO * @param loanTransactionDTO * @param office @@ -505,17 +498,16 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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(); if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + refundAmount); } else { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - refundAmount, isReversal); + refundAmount); } } @@ -523,8 +515,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF * Create a single Debit to fund source and multiple credits if applicable (loan portfolio for principal repayments, * Interest on loans for interest repayments, Income from fees for fees payment and Income from penalties for * penalty payment) - * - * In case the loan transaction is a reversal, all debits are turned into credits and vice versa */ private void createJournalEntriesForRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) { @@ -532,7 +522,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final boolean isMarkedChargeOff = loanDTO.isMarkedAsChargeOff(); if (isMarkedChargeOff) { createJournalEntriesForChargeOffLoanRepayments(loanDTO, loanTransactionDTO, office); - } else { createJournalEntriesForLoanRepayments(loanDTO, loanTransactionDTO, office); } @@ -555,7 +544,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); - final boolean isReversal = loanTransactionDTO.isReversed(); GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder(); BigDecimal totalDebitAmount = new BigDecimal(0); @@ -707,36 +695,35 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF // create credit entries for (Map.Entry creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey()); - this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - creditEntry.getValue(), isReversal, glAccount); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + creditEntry.getValue(), glAccount); } - /*** create a single debit entry (or reversal) for the entire amount **/ + /*** create a single debit entry for the entire amount **/ if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { // create debit entries for (Map.Entry debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) { GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey()); - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, loanId, transactionId, transactionDate, - debitEntry.getValue(), isReversal, glAccount); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, + debitEntry.getValue(), glAccount); } } /** - * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to - * those related to the repayment above + * Charge Refunds 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); + this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, CashAccountsForLoan.FUND_SOURCE.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } } @@ -755,7 +742,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); BigDecimal totalDebitAmount = new BigDecimal(0); @@ -763,8 +749,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO, loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, principalAmount); if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { populateDebitAccountEntry(loanProductId, principalAmount, CashAccountsForLoan.GOODWILL_CREDIT.getValue(), debitAccountMapForGoodwillCredit, paymentTypeId); @@ -773,8 +759,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS, loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, interestAmount); if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { populateDebitAccountEntry(loanProductId, interestAmount, CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), debitAccountMapForGoodwillCredit, @@ -784,9 +770,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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()); + this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_FEES.getValue(), + loanProductId, loanId, transactionId, transactionDate, feesAmount, loanTransactionDTO.getFeePayments()); if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { populateDebitAccountEntry(loanProductId, feesAmount, CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), debitAccountMapForGoodwillCredit, paymentTypeId); @@ -795,9 +780,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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()); + this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), + loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, loanTransactionDTO.getPenaltyPayments()); if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { populateDebitAccountEntry(loanProductId, penaltiesAmount, CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), debitAccountMapForGoodwillCredit, @@ -807,45 +791,44 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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); + this.helper.createCreditJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT, loanProductId, paymentTypeId, + loanId, transactionId, transactionDate, overPaymentAmount); if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { populateDebitAccountEntry(loanProductId, overPaymentAmount, CashAccountsForLoan.GOODWILL_CREDIT.getValue(), debitAccountMapForGoodwillCredit, paymentTypeId); } } - /*** create a single debit entry (or reversal) for the entire amount **/ + /*** create a single debit entry for the entire amount **/ if (loanTransactionDTO.isLoanToLoanTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else if (loanTransactionDTO.isAccountTransfer()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } else { if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) { // create debit entries for (Map.Entry debitEntry : debitAccountMapForGoodwillCredit.entrySet()) { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, debitEntry.getKey().intValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue(), isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, debitEntry.getKey(), loanProductId, paymentTypeId, + loanId, transactionId, transactionDate, debitEntry.getValue()); } } else { - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } /** - * Charge Refunds (and their reversals) have an extra refund related pair of journal entries in addition to - * those related to the repayment above + * Charge Refunds have an extra refund related pair of journal entries in addition to those related to the + * repayment above ***/ 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); + this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, CashAccountsForLoan.FUND_SOURCE.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } @@ -863,8 +846,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF /** * Create a single Debit to fund source and a single credit to "Income from Recovery" - * - * In case the loan transaction is a reversal, all debits are turned into credits and vice versa */ private void createJournalEntriesForRecoveryRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) { @@ -877,12 +858,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF 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.FUND_SOURCE.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate, - amount, isReversal); + amount); } @@ -907,18 +887,16 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final String transactionId = loanTransactionDTO.getTransactionId(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); final BigDecimal principalAmount = loanTransactionDTO.getPrincipal(); - final boolean isReversal = loanTransactionDTO.isReversed(); - // final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); if (loanTransactionDTO.getTransactionType().isInitiateTransfer()) { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, null, loanId, transactionId, transactionDate, - principalAmount, isReversal); + principalAmount); } else if (loanTransactionDTO.getTransactionType().isApproveTransfer() || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { - this.helper.createJournalEntriesAndReversalsForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), + this.helper.createJournalEntriesForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), CashAccountsForLoan.TRANSFERS_SUSPENSE.getValue(), loanProductId, null, loanId, transactionId, transactionDate, - principalAmount, isReversal); + principalAmount); } } @@ -932,7 +910,6 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF // transaction properties final String transactionId = loanTransactionDTO.getTransactionId(); final LocalDate transactionDate = loanTransactionDTO.getTransactionDate(); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); BigDecimal overpaymentAmount = loanTransactionDTO.getOverPayment(); @@ -951,8 +928,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF } JournalAmountHolder totalAmountHolder = new JournalAmountHolder(CashAccountsForLoan.FUND_SOURCE.getValue(), totalAmount); - helper.createSplitJournalEntriesAndReversalsForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, - paymentTypeId, loanId, transactionId, transactionDate, isReversal); + helper.createSplitJournalEntriesForLoan(office, currencyCode, journalAmountHolders, totalAmountHolder, loanProductId, paymentTypeId, + loanId, transactionId, transactionDate); } private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) { @@ -969,21 +946,20 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); - final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); BigDecimal totalDebitAmount = new BigDecimal(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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, principalAmount); } 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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS.getValue(), + loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount); } if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -997,9 +973,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF : chargePaymentDTO.getAmount(), chargePaymentDTO.getLoanChargeId())); } - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, - CashAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate, feesAmount, - !isReversal, chargePaymentDTOs); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_FEES.getValue(), + loanProductId, loanId, transactionId, transactionDate, feesAmount, chargePaymentDTOs); } if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) { @@ -1013,20 +988,18 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF chargePaymentDTO.getLoanChargeId())); } - this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, currencyCode, - CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), loanProductId, loanId, transactionId, transactionDate, - penaltiesAmount, !isReversal, chargePaymentDTOs); + this.helper.createDebitJournalEntryForLoanCharges(office, currencyCode, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), + loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, chargePaymentDTOs); } 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); + this.helper.createDebitJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.OVERPAYMENT.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount); } /*** create a single debit entry (or reversal) for the entire amount **/ - this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), - loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, !isReversal); - + this.helper.createCreditJournalEntryForLoan(office, currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java index 0ea9eceb6..3671ffa8c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java @@ -39,6 +39,8 @@ public interface JournalEntryWritePlatformService { CommandProcessingResult defineOpeningBalance(JsonCommand command); + void createJournalEntryForReversedLoanTransaction(LocalDate transactionDate, String loanTransactionId, Long officeId); + String revertProvisioningJournalEntries(LocalDate reversalTransactionDate, Long entityId, Integer entityType); String createProvisioningJournalEntries(ProvisioningEntry entry); diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java index 460e89e45..a0c7d9e04 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java @@ -298,6 +298,28 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa return new CommandProcessingResultBuilder().withTransactionId(reversalTransactionId).build(); } + @Override + public void createJournalEntryForReversedLoanTransaction(final LocalDate transactionDate, final String loanTransactionId, + final Long officeId) { + final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(officeId); + this.helper.checkForBranchClosures(latestGLClosure, transactionDate); + final String transactionId = AccountingProcessorHelper.LOAN_TRANSACTION_IDENTIFIER + loanTransactionId; + final List journalEntries = this.glJournalEntryRepository.findJournalEntries(transactionId, + PortfolioProductType.LOAN.getValue()); + if (journalEntries == null || journalEntries.isEmpty()) { + return; + } + for (final JournalEntry journalEntry : journalEntries) { + final JournalEntry reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getPaymentDetail(), + journalEntry.getGlAccount(), journalEntry.getCurrencyCode(), transactionId, Boolean.FALSE, transactionDate, + journalEntry.isDebitEntry() ? JournalEntryType.CREDIT : JournalEntryType.DEBIT, journalEntry.getAmount(), + journalEntry.getDescription(), journalEntry.getEntityType(), journalEntry.getEntityId(), + journalEntry.getReferenceNumber(), journalEntry.getLoanTransactionId(), journalEntry.getSavingsTransactionId(), + journalEntry.getClientTransactionId(), journalEntry.getShareTransactionId()); + helper.persistJournalEntry(reversalJournalEntry); + } + } + public String revertJournalEntry(final List journalEntries, String reversalComment) { final Long officeId = journalEntries.get(0).getOffice().getId(); final String reversalTransactionId = generateTransactionId(officeId); diff --git a/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java b/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java index 61960d64a..7280364f7 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/accounting/journalentry/CreateJournalEntriesForChargeOffLoanTest.java @@ -99,12 +99,11 @@ class CreateJournalEntriesForChargeOffLoanTest { verify(helper, times(1)).getChargeOffMappingByCodeValue(1L, PortfolioProductType.LOAN, chargeOffReasonId); verify(helper, times(1)).getLinkedGLAccountForLoanProduct(1L, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), 1L); - verify(helper, times(1)).createCreditJournalEntryOrReversalForLoan(helper.getOfficeById(1L), "USD", - AccrualAccountsForLoan.LOAN_PORTFOLIO, 1L, null, 1L, "txn-123", LocalDate.now(ZoneId.systemDefault()), - new BigDecimal("500.00"), false); - verify(helper, times(1)).createDebitJournalEntryOrReversalForLoan(helper.getOfficeById(1L), "USD", + verify(helper, times(1)).createCreditJournalEntryForLoan(helper.getOfficeById(1L), "USD", AccrualAccountsForLoan.LOAN_PORTFOLIO, 1L, + null, 1L, "txn-123", LocalDate.now(ZoneId.systemDefault()), new BigDecimal("500.00")); + verify(helper, times(1)).createDebitJournalEntryForLoan(helper.getOfficeById(1L), "USD", AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), 1L, null, 1L, "txn-123", LocalDate.now(ZoneId.systemDefault()), - new BigDecimal("500.00"), false); + new BigDecimal("500.00")); } @Test