FINERACT-2210: Fix journal entries creation issue when amount is null

This commit is contained in:
mariiaKraievska
2025-04-22 17:10:54 +02:00
committed by Adam Saghy
parent a1d51e1e34
commit a11c567413
7 changed files with 442 additions and 142 deletions
@@ -106,6 +106,7 @@ public enum DefaultLoanProduct implements LoanProduct {
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, //
LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC, //
LP2_ADV_DP_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_ADV_DP_IR_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_DISBURSEMENT_CHARGES, //
@@ -2227,6 +2227,40 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CASH_ACCOUNTING_DISBURSEMENT_CHARGES,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyCashAccountingDisbursementCharge);
// LP2 + zero-interest chargeOff behaviour + progressive loan schedule + horizontal + delinquent charge-off
// reason to GL account mapping + interest recalculation
// (LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC)
final String name90 = DefaultLoanProduct.LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC.getName();
final PostLoanProductsRequest loanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReasonIntRecalc = loanProductsRequestFactory
.defaultLoanProductsRequestLP2ChargeOffReasonToExpenseAccountMappings()//
.name(name90)//
.enableDownPayment(false)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.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<PostLoanProductsResponse> responseLoanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReasonIntRecalc = loanProductsApi
.createLoanProduct(loanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReasonIntRecalc).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC,
responseLoanProductsRequestAdvZeroInterestChargeOffProgressiveDelinquentReasonIntRecalc);
}
public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule,
@@ -117,6 +117,7 @@ public abstract class TestContextKey {
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_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC = "loanProductCreateResponseLP2AdvancedPaymentZeroInterestChargeOffDelinquentReasonInterestRecalculation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_DP_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleDPCustomPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_DP_IR_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleDPIRCustomPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_ALLOW_PARTIAL_PERIOD = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyAllowPartialPeriod";
@@ -8123,3 +8123,225 @@ Feature: Charge-off
And Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 23 January 2024 | Flat | 5.0 | 0.0 | 5.0 | 0.0 |
@TestRailId:C3611
Scenario: As a user I want to verify that backdated transactions are allowed after charge-off and backdated full repayment, interestRecalculation = false
When Admin sets the business date to "11 March 2025"
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 | 11 March 2025 | 500 | 7 | DECLINING_BALANCE | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "11 March 2025" with "500" amount and expected disbursement date on "11 March 2025"
And Admin successfully disburse the loan on "11 March 2025" with "500" EUR transaction amount
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 2 | 30 | 11 May 2025 | | 335.37 | 82.6 | 2.4 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 3 | 31 | 11 June 2025 | | 252.36 | 83.01 | 1.99 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 4 | 30 | 11 July 2025 | | 168.81 | 83.55 | 1.45 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 5 | 31 | 11 August 2025 | | 84.81 | 84.0 | 1.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 6 | 31 | 11 September 2025| | 0.0 | 84.81 | 0.5 | 0.0 | 0.0 | 85.31 | 0.0 | 0.0 | 0.0 | 0.0 | 85.31 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 10.31 | 0.0 | 0 | 510.31 | 0.0 | 0 | 0 | 0.0 | 510.31 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
Then Admin can successfully set Fraud flag to the loan
When Admin runs inline COB job for Loan
When Admin sets the business date to "14 April 2025"
And Admin does charge-off the loan with reason "DELINQUENT" on "14 April 2025"
Then Loan marked as charged-off on "14 April 2025"
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 2 | 30 | 11 May 2025 | | 333.21 | 84.76 | 0.24 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 3 | 31 | 11 June 2025 | | 248.21 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 4 | 30 | 11 July 2025 | | 163.21 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 5 | 31 | 11 August 2025 | | 78.21 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 6 | 31 | 11 September 2025 | | 0.0 | 78.21 | 0.0 | 0.0 | 0.0 | 78.21 | 0.0 | 0.0 | 0.0 | 0.0 | 78.21 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 3.21 | 0.0 | 0 | 503.21 | 0 | 0 | 0 | 0.0 | 503.21 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 14 April 2025 | Accrual | 3.21 | 0.0 | 3.21 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 503.21 | 500.0 | 3.21 | 0.0 | 0.0 | 0.0 | false | false |
When Admin runs inline COB job for Loan
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 335.37 | 82.6 | 2.4 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 252.36 | 83.01 | 1.99 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 168.81 | 83.55 | 1.45 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 84.81 | 84.0 | 1.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | | 0.0 | 84.81 | 0.0 | 0.0 | 0.0 | 84.81 | 75.0 | 75.0 | 0.0 | 0.0 | 9.81 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 9.81 | 0.0 | 0 | 509.81 | 500.0 | 415.0 | 85.0 | 0.0 | 9.81 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 490.19 | 9.81 | 0.0 | 0.0 | 9.81 | false | false |
| 14 April 2025 | Accrual | 3.21 | 0.0 | 3.21 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.24 | 0.0 | 0.24 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 9.81 | 9.81 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
When Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 335.37 | 82.6 | 2.4 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 252.36 | 83.01 | 1.99 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 168.81 | 83.55 | 1.45 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 84.81 | 84.0 | 1.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | 13 April 2025 | 0.0 | 84.81 | 0.5 | 0.0 | 0.0 | 85.31 | 85.31 | 85.31 | 0.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 10.31 | 0.0 | 0 | 510.31 | 510.31 | 425.31 | 85.0 | 0.0 | 0.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 490.19 | 9.81 | 0.0 | 0.0 | 9.81 | false | false |
| 13 April 2025 | Merchant Issued Refund | 500.0 | 9.81 | 0.5 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual | 3.21 | 0.0 | 3.21 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.24 | 0.0 | 0.24 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
When Customer makes "GOODWILL_CREDIT" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 335.37 | 82.6 | 2.4 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 252.36 | 83.01 | 1.99 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 168.81 | 83.55 | 1.45 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 84.81 | 84.0 | 1.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | 13 April 2025 | 0.0 | 84.81 | 0.5 | 0.0 | 0.0 | 85.31 | 85.31 | 85.31 | 0.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 10.31 | 0.0 | 0 | 510.31 | 510.31 | 425.31 | 85.0 | 0.0 | 0.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 490.19 | 9.81 | 0.0 | 0.0 | 9.81 | false | false |
| 13 April 2025 | Merchant Issued Refund | 500.0 | 9.81 | 0.5 | 0.0 | 0.0 | 0.0 | false | false |
| 13 April 2025 | Goodwill Credit | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual | 3.21 | 0.0 | 3.21 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.24 | 0.0 | 0.24 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
@TestRailId:C3612
Scenario: As a user I want to verify that backdated transactions are allowed after charge-off and backdated full repayment, interestRecalculation = true
When Admin sets the business date to "11 March 2025"
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_INTEREST_RECALC | 11 March 2025 | 500 | 7 | DECLINING_BALANCE | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "11 March 2025" with "500" amount and expected disbursement date on "11 March 2025"
And Admin successfully disburse the loan on "11 March 2025" with "500" EUR transaction amount
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 2 | 30 | 11 May 2025 | | 335.37 | 82.6 | 2.4 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 3 | 31 | 11 June 2025 | | 252.36 | 83.01 | 1.99 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 4 | 30 | 11 July 2025 | | 168.81 | 83.55 | 1.45 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 5 | 31 | 11 August 2025 | | 84.81 | 84.0 | 1.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 6 | 31 | 11 September 2025| | 0.0 | 84.81 | 0.5 | 0.0 | 0.0 | 85.31 | 0.0 | 0.0 | 0.0 | 0.0 | 85.31 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 10.31 | 0.0 | 0 | 510.31 | 0.0 | 0 | 0 | 0.0 | 510.31 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
Then Admin can successfully set Fraud flag to the loan
When Admin runs inline COB job for Loan
When Admin sets the business date to "14 April 2025"
And Admin does charge-off the loan with reason "DELINQUENT" on "14 April 2025"
Then Loan marked as charged-off on "14 April 2025"
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 2 | 30 | 11 May 2025 | | 333.26 | 84.71 | 0.29 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 3 | 31 | 11 June 2025 | | 248.26 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 4 | 30 | 11 July 2025 | | 163.26 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 5 | 31 | 11 August 2025 | | 78.26 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 | 0.0 | 0.0 | 85.0 |
| 6 | 31 | 11 September 2025 | | 0.0 | 78.26 | 0.0 | 0.0 | 0.0 | 78.26 | 0.0 | 0.0 | 0.0 | 0.0 | 78.26 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 3.26 | 0.0 | 0 | 503.26 | 0.0 | 0 | 0 | 0.0 | 503.26 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 14 April 2025 | Accrual | 3.26 | 0.0 | 3.26 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 503.26 | 500.0 | 3.26 | 0.0 | 0.0 | 0.0 | false | false |
When Admin runs inline COB job for Loan
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 333.16 | 84.81 | 0.19 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 248.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 163.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 78.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | | 0.0 | 78.16 | 0.0 | 0.0 | 0.0 | 78.16 | 75.0 | 75.0 | 0.0 | 0.0 | 3.16 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 3.16 | 0.0 | 0 | 503.16 | 500.0 | 415.0 | 85.0 | 0.0 | 3.16 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 496.84 | 3.16 | 0.0 | 0.0 | 3.16 | false | false |
| 14 April 2025 | Accrual | 3.26 | 0.0 | 3.26 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.1 | 0.0 | 0.1 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 3.16 | 3.16 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
When Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 333.16 | 84.81 | 0.19 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 248.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 163.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 78.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | 13 April 2025 | 0.0 | 78.16 | 0.0 | 0.0 | 0.0 | 78.16 | 78.16 | 78.16 | 0.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 3.16 | 0.0 | 0 | 503.16 | 503.16 | 418.16 | 85.0 | 0.0 | 0.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 496.84 | 3.16 | 0.0 | 0.0 | 3.16 | false | false |
| 13 April 2025 | Merchant Issued Refund | 500.0 | 3.16 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual | 3.26 | 0.0 | 3.26 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.1 | 0.0 | 0.1 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
When Customer makes "GOODWILL_CREDIT" transaction with "AUTOPAY" payment type on "13 April 2025" with 500 EUR transaction amount and system-generated Idempotency key
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 | Waived | Outstanding |
| | | 11 March 2025 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | | |
| 1 | 31 | 11 April 2025 | 13 April 2025 | 417.97 | 82.03 | 2.97 | 0.0 | 0.0 | 85.0 | 85.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 30 | 11 May 2025 | 13 April 2025 | 333.16 | 84.81 | 0.19 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 3 | 31 | 11 June 2025 | 13 April 2025 | 248.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 4 | 30 | 11 July 2025 | 13 April 2025 | 163.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 5 | 31 | 11 August 2025 | 13 April 2025 | 78.16 | 85.0 | 0.0 | 0.0 | 0.0 | 85.0 | 85.0 | 85.0 | 0.0 | 0.0 | 0.0 |
| 6 | 31 | 11 September 2025 | 13 April 2025 | 0.0 | 78.16 | 0.0 | 0.0 | 0.0 | 78.16 | 78.16 | 78.16 | 0.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Waived | Outstanding |
| 500.0 | 3.16 | 0.0 | 0 | 503.16 | 503.16 | 418.16 | 85.0 | 0.0 | 0.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 11 March 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
| 13 April 2025 | Repayment | 500.0 | 496.84 | 3.16 | 0.0 | 0.0 | 3.16 | false | false |
| 13 April 2025 | Merchant Issued Refund | 500.0 | 3.16 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 13 April 2025 | Goodwill Credit | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual | 3.26 | 0.0 | 3.26 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Accrual Adjustment | 0.1 | 0.0 | 0.1 | 0.0 | 0.0 | 0.0 | false | false |
| 14 April 2025 | Charge-off | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
@@ -1400,20 +1400,25 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate())) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
currentInstallment.getDueDate(), transactionDate, true).getAmount();
currentInstallment.updateInterestCharged(newInterest);
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
currentInstallment.updateInterestCharged(newInterest);
}
} else {
final BigDecimal totalInterest = currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()).getAmount();
final long totalDaysInPeriod = ChronoUnit.DAYS.between(currentInstallment.getFromDate(), currentInstallment.getDueDate());
final long daysTillChargeOff = ChronoUnit.DAYS.between(currentInstallment.getFromDate(), transactionDate);
if (totalInterest.compareTo(BigDecimal.ZERO) > 0) {
final long totalDaysInPeriod = ChronoUnit.DAYS.between(currentInstallment.getFromDate(),
currentInstallment.getDueDate());
final long daysTillChargeOff = ChronoUnit.DAYS.between(currentInstallment.getFromDate(), transactionDate);
final MathContext mc = MoneyHelper.getMathContext();
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(),
totalInterest.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc),
mc);
final MathContext mc = MoneyHelper.getMathContext();
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(), totalInterest
.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc);
currentInstallment.updateInterestCharged(interestTillChargeOff.getAmount());
currentInstallment.updateInterestCharged(interestTillChargeOff.getAmount());
}
}
currentInstallment.updateDueDate(transactionDate);
@@ -1478,25 +1483,27 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void handleZeroInterestChargeOff(final LoanTransaction loanTransaction, final TransactionCtx transactionCtx) {
final LocalDate transactionDate = loanTransaction.getTransactionDate();
final List<LoanRepaymentScheduleInstallment> installments = transactionCtx.getInstallments();
final MonetaryCurrency currency = loanTransaction.getLoan().getCurrency();
if (!installments.isEmpty()) {
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
installments.stream().filter(installment -> !installment.getFromDate().isAfter(transactionDate)
&& installment.getDueDate().isAfter(transactionDate)).forEach(installment -> {
final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
installment.getDueDate(), transactionDate, true).getAmount();
final BigDecimal interestRemoved = installment.getInterestCharged().subtract(newInterest);
installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved));
installment.updateInterestCharged(newInterest);
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
final BigDecimal interestRemoved = installment.getInterestCharged().subtract(newInterest);
installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved));
installment.updateInterestCharged(newInterest);
}
});
progressiveTransactionCtx.setChargedOff(true);
} else {
calculatePartialPeriodInterest(transactionCtx, transactionDate);
}
final MonetaryCurrency currency = loanTransaction.getLoan().getCurrency();
installments.stream()
.filter(installment -> installment.getFromDate().isAfter(transactionDate) && !installment.isObligationsMet())
.forEach(installment -> {
@@ -1538,16 +1545,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
.filter(installment -> !installment.getFromDate().isAfter(chargeOffDate) && installment.getDueDate().isAfter(chargeOffDate))
.forEach(installment -> {
final BigDecimal totalInterest = installment.getInterestOutstanding(transactionCtx.getCurrency()).getAmount();
final long totalDaysInPeriod = ChronoUnit.DAYS.between(installment.getFromDate(), installment.getDueDate());
final long daysTillChargeOff = ChronoUnit.DAYS.between(installment.getFromDate(), chargeOffDate);
if (totalInterest.compareTo(BigDecimal.ZERO) > 0) {
final long totalDaysInPeriod = ChronoUnit.DAYS.between(installment.getFromDate(), installment.getDueDate());
final long daysTillChargeOff = ChronoUnit.DAYS.between(installment.getFromDate(), chargeOffDate);
final MathContext mc = MoneyHelper.getMathContext();
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(), totalInterest
.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc);
final MathContext mc = MoneyHelper.getMathContext();
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(), totalInterest
.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc);
final BigDecimal interestRemoved = totalInterest.subtract(interestTillChargeOff.getAmount());
installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved));
installment.updateInterestCharged(interestTillChargeOff.getAmount());
final BigDecimal interestRemoved = totalInterest.subtract(interestTillChargeOff.getAmount());
installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved));
installment.updateInterestCharged(interestTillChargeOff.getAmount());
}
});
}
@@ -60,6 +60,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductT
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.infrastructure.event.business.domain.journalentry.LoanJournalEntryCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.office.domain.Office;
@@ -453,13 +454,19 @@ public class AccountingProcessorHelper {
JournalAmountHolder totalAccountHolder, Long loanProductId, Long paymentTypeId, Long loanId, String transactionId,
LocalDate transactionDate) {
splitAccountsHolder.forEach(journalItemHolder -> {
final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, journalItemHolder.getAccountType(), paymentTypeId);
createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate,
journalItemHolder.getAmount());
if (MathUtil.isGreaterThanZero(journalItemHolder.getAmount())) {
final GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, journalItemHolder.getAccountType(),
paymentTypeId);
createDebitJournalEntryForLoan(office, currencyCode, account, loanId, transactionId, transactionDate,
journalItemHolder.getAmount());
}
});
final GLAccount totalAccount = getLinkedGLAccountForLoanProduct(loanProductId, totalAccountHolder.getAccountType(), paymentTypeId);
createCreditJournalEntryForLoan(office, currencyCode, totalAccount, loanId, transactionId, transactionDate,
totalAccountHolder.getAmount());
if (MathUtil.isGreaterThanZero(totalAccountHolder.getAmount())) {
final GLAccount totalAccount = getLinkedGLAccountForLoanProduct(loanProductId, totalAccountHolder.getAccountType(),
paymentTypeId);
createCreditJournalEntryForLoan(office, currencyCode, totalAccount, loanId, transactionId, transactionDate,
totalAccountHolder.getAmount());
}
}
public void createCreditJournalEntryForLoan(final Office office, final String currencyCode,
@@ -147,75 +147,79 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
if (isMarkedAsChargeOff) {
// ChargeOFF
// principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// handle overpayment
if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPayment)) {
populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
} else {
// principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
// handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(),
glAccountBalanceHolder);
}
// handle overpayment
if (overPayment != null && overPayment.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPayment)) {
populateCreditDebitMaps(loanProductId, overPayment, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(), glAccountBalanceHolder);
}
}
// create credit entries
for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
}
}
// create debit entries
for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, debitEntry.getValue(),
glAccount);
if (MathUtil.isGreaterThanZero(debitEntry.getValue())) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
debitEntry.getValue(), glAccount);
}
}
}
@@ -241,14 +245,15 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
ProductToGLAccountMapping mapping = chargeOffReasonCodeValue != null
? helper.getChargeOffMappingByCodeValue(loanProductId, PortfolioProductType.LOAN, chargeOffReasonCodeValue)
: null;
if (mapping != null) {
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
glAccountBalanceHolder.addToCredit(accountCredit, principalAmount);
glAccountBalanceHolder.addToDebit(mapping.getGlAccount(), principalAmount);
} else {
// principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
if (mapping != null) {
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
glAccountBalanceHolder.addToCredit(accountCredit, principalAmount);
glAccountBalanceHolder.addToDebit(mapping.getGlAccount(), principalAmount);
} else {
// principal payment
if (isMarkedFraud) {
populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), glAccountBalanceHolder);
@@ -259,43 +264,48 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
// interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId, AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), glAccountBalanceHolder);
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), glAccountBalanceHolder);
}
// handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), glAccountBalanceHolder);
}
// create credit entries
for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
}
}
// create debit entries
for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, debitEntry.getValue(),
glAccount);
if (MathUtil.isGreaterThanZero(debitEntry.getValue())) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
debitEntry.getValue(), glAccount);
}
}
}
private void populateCreditDebitMaps(Long loanProductId, BigDecimal transactionPartAmount, Long paymentTypeId,
Integer creditAccountType, Integer debitAccountType, GLAccountBalanceHolder glAccountBalanceHolder) {
// Resolve Credit
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, paymentTypeId);
glAccountBalanceHolder.addToCredit(accountCredit, transactionPartAmount);
// Resolve Debit
GLAccount accountDebit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, debitAccountType, paymentTypeId);
glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
if (MathUtil.isGreaterThanZero(transactionPartAmount)) {
// Resolve Credit
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, paymentTypeId);
glAccountBalanceHolder.addToCredit(accountCredit, transactionPartAmount);
// Resolve Debit
GLAccount accountDebit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, debitAccountType, paymentTypeId);
glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
}
}
private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
@@ -329,7 +339,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
// handle principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), paymentTypeId);
@@ -337,7 +347,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), paymentTypeId);
@@ -351,7 +361,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), paymentTypeId);
@@ -364,7 +374,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle penalty payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), paymentTypeId);
@@ -377,7 +387,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle overpayment
if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
paymentTypeId);
@@ -390,11 +400,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
if (MathUtil.isGreaterThanZero(entry.getValue())) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
}
}
if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId();
Integer accountMappingTypeId;
if (loanTransactionDTO.getLoanChargeData().isPenalty()) {
@@ -428,7 +440,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
// handle principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
@@ -436,7 +448,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
@@ -449,7 +461,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId);
@@ -462,7 +474,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle penalties payment
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId);
@@ -475,7 +487,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle overpayment
if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
paymentTypeId);
@@ -488,11 +500,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
if (MathUtil.isGreaterThanZero(entry.getValue())) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
}
}
if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
Long chargeId = loanTransactionDTO.getLoanChargeData().getChargeId();
Integer accountMappingTypeId;
if (loanTransactionDTO.getLoanChargeData().isPenalty()) {
@@ -538,10 +552,12 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal penaltyPaid = Objects.isNull(loanTransactionDTO.getPenaltyPaid()) ? BigDecimal.ZERO
: loanTransactionDTO.getPenaltyPaid();
helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, amount);
if (MathUtil.isGreaterThanZero(amount)) {
helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, amount);
}
if (overpaidAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overpaidAmount)) {
helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, overpaidAmount);
}
@@ -617,7 +633,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal overpaymentPortion = loanTransactionDTO.getOverPayment() != null ? loanTransactionDTO.getOverPayment()
: BigDecimal.ZERO;
final BigDecimal principalPortion = loanTransactionDTO.getAmount().subtract(overpaymentPortion);
final BigDecimal loanTransactionDTOAmount = loanTransactionDTO.getAmount();
final BigDecimal principalPortion = loanTransactionDTOAmount.subtract(overpaymentPortion);
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for the disbursement
@@ -630,15 +647,17 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, overpaymentPortion);
}
if (loanTransactionDTO.isLoanToLoanTransfer()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount());
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount());
} else {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTO.getAmount());
if (MathUtil.isGreaterThanZero(loanTransactionDTOAmount)) {
if (loanTransactionDTO.isLoanToLoanTransfer()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTOAmount);
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTOAmount);
} else {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, loanTransactionDTOAmount);
}
}
}
@@ -705,7 +724,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
BigDecimal totalDebitAmount = new BigDecimal(0);
// principal payment
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
if (isMarkedFraud) {
@@ -747,7 +766,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// interest payment
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
@@ -777,7 +796,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle fees payment
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId,
@@ -816,7 +835,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle penalties
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
@@ -853,7 +872,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// overpayment
if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
if (loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
@@ -876,12 +895,14 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// create credit entries
for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
creditEntry.getValue(), glAccount);
}
}
if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
if (writeOff) {
this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
@@ -907,7 +928,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* 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 (MathUtil.isGreaterThanZero(totalDebitAmount)) {
if (loanTransactionDTO.getTransactionType().isChargeRefund()) {
Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
@@ -940,7 +961,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
Map<Integer, BigDecimal> debitAccountMapForGoodwillCredit = new LinkedHashMap<>();
// handle principal payment or writeOff
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
@@ -952,7 +973,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle interest payment of writeOff
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
@@ -970,10 +991,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle fees payment of writeOff
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
if (isIncomeFromFee) {
this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(),
loanProductId, loanId, transactionId, transactionDate, feesAmount, loanTransactionDTO.getFeePayments());
@@ -994,7 +1013,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
// handle penalties payment of writeOff
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
if (isIncomeFromFee) {
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
@@ -1023,7 +1042,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
paymentTypeId);
@@ -1040,14 +1059,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
if (MathUtil.isGreaterThanZero(entry.getValue())) {
this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
entry.getKey());
}
}
/**
* Single DEBIT transaction for write-offs or Repayments
***/
if (totalDebitAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
if (writeOff) {
this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
@@ -1078,7 +1099,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* 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()) {
if (MathUtil.isGreaterThanZero(totalDebitAmount) && loanTransactionDTO.getTransactionType().isChargeRefund()) {
Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
@@ -1122,10 +1143,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal amount = loanTransactionDTO.getAmount();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount);
if (MathUtil.isGreaterThanZero(amount)) {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount);
}
}
/**
@@ -1156,7 +1178,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for recognizing interest
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
if (transactionType.isAccrualAdjustment()) {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(),
AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
@@ -1168,7 +1190,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
// create journal entries for the fees application
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
if (transactionType.isAccrualAdjustment()) {
this.helper.createJournalEntriesForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(),
AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), loanProductId, loanId, transactionId, transactionDate,
@@ -1180,7 +1202,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
}
// create journal entries for the penalties application
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
if (transactionType.isAccrualAdjustment()) {
this.helper.createJournalEntriesForLoanCharges(office, currencyCode,
AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
@@ -1205,14 +1227,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal refundAmount = loanTransactionDTO.getAmount();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount);
} else {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount);
if (MathUtil.isGreaterThanZero(refundAmount)) {
if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, refundAmount);
} else {
this.helper.createJournalEntriesForLoan(office, currencyCode, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
refundAmount);
}
}
}
@@ -1241,12 +1265,12 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
BigDecimal totalAmount = BigDecimal.ZERO;
List<JournalAmountHolder> journalAmountHolders = new ArrayList<>();
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalAmount = totalAmount.add(principalAmount);
journalAmountHolders
.add(new JournalAmountHolder(determineAccrualAccountForCBR(isMarkedChargeOff, isMarkedFraud, false), principalAmount));
}
if (overpaymentAmount != null && overpaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overpaymentAmount)) {
totalAmount = totalAmount.add(overpaymentAmount);
journalAmountHolders
.add(new JournalAmountHolder(determineAccrualAccountForCBR(isMarkedChargeOff, isMarkedFraud, true), overpaymentAmount));
@@ -1293,19 +1317,19 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
BigDecimal totalDebitAmount = new BigDecimal(0);
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(principalAmount)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount);
}
if (interestAmount != null && interestAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(interestAmount)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.INTEREST_ON_LOANS.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, interestAmount);
}
if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(feesAmount)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
List<ChargePaymentDTO> chargePaymentDTOs = new ArrayList<>();
@@ -1320,7 +1344,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
loanProductId, loanId, transactionId, transactionDate, feesAmount, chargePaymentDTOs);
}
if (penaltiesAmount != null && penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
List<ChargePaymentDTO> chargePaymentDTOs = new ArrayList<>();
@@ -1334,14 +1358,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
loanProductId, loanId, transactionId, transactionDate, penaltiesAmount, chargePaymentDTOs);
}
if (overPaymentAmount != null && overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
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.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
/*** create a single debit entry (or reversal) for the entire amount **/
this.helper.createCreditJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
}
}
}