FINERACT-2081: Fix Accrual activity - reverse and replayed couple of times after backdated repayment
This commit is contained in:
committed by
Adam Saghy
parent
991a2c1af4
commit
7642530393
@@ -30,3 +30,6 @@ fineract-provider/src/main/generated/
|
|||||||
|
|
||||||
**/out/
|
**/out/
|
||||||
gradleExp/
|
gradleExp/
|
||||||
|
|
||||||
|
.run/
|
||||||
|
.java-version
|
||||||
+1
-1
@@ -120,8 +120,8 @@ public abstract class TestContextKey {
|
|||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALCULATION_DAILY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRecalculationDaily";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALCULATION_DAILY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRecalculationDaily";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationAccelerateMaturityChargeOffBehaviour";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationAccelerateMaturityChargeOffBehaviour";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030AccrualActivity";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030AccrualActivity";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentAccelerateMaturityChargeOffBehaviour";
|
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmi36030AccrualActivity";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_360_30_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmi36030AccrualActivity";
|
||||||
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_ACCELERATE_MATURITY_CHARGE_OFF_BEHAVIOUR = "loanProductCreateResponseLP2AdvancedPaymentAccelerateMaturityChargeOffBehaviour";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmiActualActualAccrualActivity";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_RECOGNITION_DISBURSEMENT_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestRecognitionDisbursementDailyEmiActualActualAccrualActivity";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_PENALTY_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestPenaltyFeePrincipal";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_PENALTY_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestPenaltyFeePrincipal";
|
||||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestFeePrincipal";
|
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_EMI_360_30_CHARGEBACK_INTEREST_FEE_PRINCIPAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalcEmi36030ChargebackInterestFeePrincipal";
|
||||||
|
|||||||
@@ -4584,7 +4584,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 1 | 29 | 29 February 2024 | | 1671.5 | 328.5 | 11.67 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 1 | 29 | 29 February 2024 | | 1671.5 | 328.5 | 11.67 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 2 | 31 | 31 March 2024 | | 1341.14 | 330.36 | 9.81 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 2 | 31 | 31 March 2024 | | 1341.14 | 330.36 | 9.81 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 3 | 30 | 30 April 2024 | | 1008.79 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 3 | 30 | 30 April 2024 | | 1008.79 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 4 | 31 | 31 May 2024 | | 674.5 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 4 | 31 | 31 May 2024 | | 674.5 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 5 | 30 | 30 June 2024 | | 338.26 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 5 | 30 | 30 June 2024 | | 338.26 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 6 | 31 | 31 July 2024 | | 0.0 | 338.26 | 1.97 | 0.0 | 0.0 | 340.23 | 0.0 | 0.0 | 0.0 | 340.23 |
|
| 6 | 31 | 31 July 2024 | | 0.0 | 338.26 | 1.97 | 0.0 | 0.0 | 340.23 | 0.0 | 0.0 | 0.0 | 340.23 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
@@ -4629,11 +4629,11 @@ Feature: LoanAccrualActivity
|
|||||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| | | 31 January 2024 | | 2000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 31 January 2024 | | 2000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 |
|
| 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 |
|
||||||
| 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 3 | 30 | 30 April 2024 | | 1007.76 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 3 | 30 | 30 April 2024 | | 1007.76 | 332.35 | 7.82 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 4 | 31 | 31 May 2024 | | 673.47 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 4 | 31 | 31 May 2024 | | 673.47 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 5 | 30 | 30 June 2024 | | 337.23 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 5 | 30 | 30 June 2024 | | 337.23 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 6 | 31 | 31 July 2024 | | 0.0 | 337.23 | 1.97 | 0.0 | 0.0 | 339.2 | 0.0 | 0.0 | 0.0 | 339.2 |
|
| 6 | 31 | 31 July 2024 | | 0.0 | 337.23 | 1.97 | 0.0 | 0.0 | 339.2 | 0.0 | 0.0 | 0.0 | 339.2 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||||
| 2000.0 | 40.05 | 0.0 | 0.0 | 2040.05 | 340.17 | 340.17 | 0.0 | 1699.88 |
|
| 2000.0 | 40.05 | 0.0 | 0.0 | 2040.05 | 340.17 | 340.17 | 0.0 | 1699.88 |
|
||||||
@@ -4680,7 +4680,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 |
|
| 1 | 29 | 29 February 2024 | 15 February 2024 | 1665.86 | 334.14 | 6.03 | 0.0 | 0.0 | 340.17 | 340.17 | 340.17 | 0.0 | 0.0 |
|
||||||
| 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 2 | 31 | 31 March 2024 | | 1340.11 | 325.75 | 14.42 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 3 | 30 | 30 April 2024 | | 1007.82 | 332.29 | 7.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 3 | 30 | 30 April 2024 | | 1007.82 | 332.29 | 7.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 4 | 31 | 31 May 2024 | | 673.53 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 4 | 31 | 31 May 2024 | | 673.53 | 334.29 | 5.88 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 5 | 30 | 30 June 2024 | | 337.29 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
| 5 | 30 | 30 June 2024 | | 337.29 | 336.24 | 3.93 | 0.0 | 0.0 | 340.17 | 0.0 | 0.0 | 0.0 | 340.17 |
|
||||||
| 6 | 31 | 31 July 2024 | | 0.0 | 337.29 | 1.97 | 0.0 | 0.0 | 339.26 | 0.0 | 0.0 | 0.0 | 339.26 |
|
| 6 | 31 | 31 July 2024 | | 0.0 | 337.29 | 1.97 | 0.0 | 0.0 | 339.26 | 0.0 | 0.0 | 0.0 | 339.26 |
|
||||||
Then Loan Repayment schedule has the following data in Total row:
|
Then Loan Repayment schedule has the following data in Total row:
|
||||||
@@ -4721,7 +4721,7 @@ Feature: LoanAccrualActivity
|
|||||||
| 28 February 2024 | Accrual | 0.41 | 0.0 | 0.41 | 0.0 | 0.0 | 0.0 | false | false |
|
| 28 February 2024 | Accrual | 0.41 | 0.0 | 0.41 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 29 February 2024 | Accrual | 0.38 | 0.0 | 0.38 | 0.0 | 0.0 | 0.0 | false | false |
|
| 29 February 2024 | Accrual | 0.38 | 0.0 | 0.38 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 29 February 2024 | Accrual Activity | 6.03 | 0.0 | 6.03 | 0.0 | 0.0 | 0.0 | false | true |
|
| 29 February 2024 | Accrual Activity | 6.03 | 0.0 | 6.03 | 0.0 | 0.0 | 0.0 | false | true |
|
||||||
| 01 March 2024 | Accrual Adjustment | 0.69 | 0.0 | 0.69 | 0.0 | 0.0 | 0.0 | false | false |
|
| 01 March 2024 | Accrual Adjustment | 0.69 | 0.0 | 0.69 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 02 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false |
|
| 02 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 03 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false |
|
| 03 March 2024 | Accrual | 0.31 | 0.0 | 0.31 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 04 March 2024 | Accrual | 0.32 | 0.0 | 0.32 | 0.0 | 0.0 | 0.0 | false | false |
|
| 04 March 2024 | Accrual | 0.32 | 0.0 | 0.32 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
|||||||
+5
@@ -1058,4 +1058,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
public enum PaymentAction {
|
public enum PaymentAction {
|
||||||
PAY, UNPAY
|
PAY, UNPAY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTransactionDateWithinPeriod(LocalDate referenceDate) {
|
||||||
|
return DateUtils.isAfter(referenceDate, getFromDate()) && !DateUtils.isAfter(referenceDate, getDueDate());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -1035,4 +1035,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
|
|
||||||
// TODO missing hashCode(), equals(Object obj), but probably OK as long as
|
// TODO missing hashCode(), equals(Object obj), but probably OK as long as
|
||||||
// this is never stored in a Collection.
|
// this is never stored in a Collection.
|
||||||
|
|
||||||
|
public void updateTransactionDate(final LocalDate transactionDate) {
|
||||||
|
this.dateOf = transactionDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -239,7 +239,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
Money interestPortion = currentInstallment.getInterestCharged(currency);
|
Money interestPortion = currentInstallment.getInterestCharged(currency);
|
||||||
Money feeChargesPortion = currentInstallment.getFeeChargesCharged(currency);
|
Money feeChargesPortion = currentInstallment.getFeeChargesCharged(currency);
|
||||||
Money penaltyChargesPortion = currentInstallment.getPenaltyChargesCharged(currency);
|
Money penaltyChargesPortion = currentInstallment.getPenaltyChargesCharged(currency);
|
||||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
if (interestPortion.plus(feeChargesPortion).plus(penaltyChargesPortion).isZero()) {
|
||||||
|
loanTransaction.reverse();
|
||||||
|
} else {
|
||||||
|
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
final Loan loan = loanTransaction.getLoan();
|
||||||
|
if ((loan.isClosedObligationsMet() || loan.isOverPaid()) && currentInstallment.isObligationsMet()
|
||||||
|
&& currentInstallment.isTransactionDateWithinPeriod(currentInstallment.getObligationsMetOnDate())) {
|
||||||
|
loanTransaction.updateTransactionDate(currentInstallment.getObligationsMetOnDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-19
@@ -417,7 +417,6 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
transaction(6.48, "Accrual Activity", "17 November 2024"), //
|
transaction(6.48, "Accrual Activity", "17 November 2024"), //
|
||||||
transaction(4.75, "Accrual Activity", "17 December 2024"), //
|
transaction(4.75, "Accrual Activity", "17 December 2024"), //
|
||||||
transaction(18.31, "Accrual Activity", "17 January 2025")); //
|
transaction(18.31, "Accrual Activity", "17 January 2025")); //
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,7 +718,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
|
|
||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
disbursementDay));
|
disbursementDay, BigDecimal.ZERO));
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
@@ -798,7 +797,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
|
|
||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
disbursementDay));
|
disbursementDay, BigDecimal.ZERO));
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
@@ -850,7 +849,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
runAt(creationBusinessDay, () -> {
|
runAt(creationBusinessDay, () -> {
|
||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation();
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
disbursementDay));
|
disbursementDay, BigDecimal.ZERO));
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
@@ -955,7 +954,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||||
|
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
disbursementDay, disbursementDay2));
|
disbursementDay, disbursementDay2, BigDecimal.ZERO));
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
@@ -1014,7 +1013,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||||
|
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
disbursementDay, disbursementDay2));
|
disbursementDay, disbursementDay2, BigDecimal.ZERO));
|
||||||
|
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
@@ -1119,7 +1118,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
runAt(creationBusinessDay, () -> {
|
runAt(creationBusinessDay, () -> {
|
||||||
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(true);
|
||||||
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000),
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000),
|
||||||
disbursementDay, disbursementDay2));
|
disbursementDay, disbursementDay2, BigDecimal.ZERO));
|
||||||
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
|
|
||||||
@@ -1391,6 +1390,41 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReverseAndReplayedCoupleOfTimesAfterBackdatedRepayment() {
|
||||||
|
final String disbursementDay = "01 January 2025";
|
||||||
|
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||||
|
runAt(disbursementDay, () -> {
|
||||||
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocationInterestRecalculation(true);
|
||||||
|
loanId.set(applyForLoanApplicationAdvancedPaymentAllocation(client.getClientId(), localLoanProductId, BigDecimal.valueOf(800),
|
||||||
|
disbursementDay, disbursementDay, BigDecimal.valueOf(0.3)));
|
||||||
|
|
||||||
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(800))
|
||||||
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||||
|
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(800.0)).locale("en"));
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("02 February 2025", () -> {
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||||
|
verifyTransactions(loanId.get(),
|
||||||
|
transaction(10.60, "Accrual Activity", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(10.60, "Accrual", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(800.0, "Disbursement", disbursementDay, 800.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false));
|
||||||
|
|
||||||
|
loanTransactionHelper.makeLoanRepayment("31 January 2025", 900.0F, loanId.get().intValue());
|
||||||
|
|
||||||
|
verifyTransactions(loanId.get(),
|
||||||
|
transaction(0.34, "Accrual Adjustment", "02 February 2025", 0.0, 0.0, 0.34, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(10.60, "Accrual", "01 February 2025", 0.0, 0.0, 10.60, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(10.26, "Accrual Activity", "31 January 2025", 0.0, 0.0, 10.26, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(900.0, "Repayment", "31 January 2025", 0.0, 800, 10.26, 0.0, 0.0, 0.0, 89.74, false), //
|
||||||
|
transaction(800.0, "Disbursement", disbursementDay, 800.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false));
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private PostLoanProductsRequest loanProductsRequestInterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity() {
|
private PostLoanProductsRequest loanProductsRequestInterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity() {
|
||||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||||
@@ -1439,6 +1473,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
.minInterestRatePerPeriod(0.0)//
|
.minInterestRatePerPeriod(0.0)//
|
||||||
.interestRatePerPeriod(12.0)//
|
.interestRatePerPeriod(12.0)//
|
||||||
.maxInterestRatePerPeriod(30.0)//
|
.maxInterestRatePerPeriod(30.0)//
|
||||||
|
.interestRateFrequencyType(2)// Month
|
||||||
.interestRateFrequencyType(3)//
|
.interestRateFrequencyType(3)//
|
||||||
.repaymentEvery(30)//
|
.repaymentEvery(30)//
|
||||||
.repaymentFrequencyType(0L)//
|
.repaymentFrequencyType(0L)//
|
||||||
@@ -1631,19 +1666,28 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(boolean isMultiDisburse) {
|
private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocation(boolean isMultiDisburse) {
|
||||||
|
Long resourceId = loanTransactionHelper
|
||||||
|
.createLoanProduct(loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(isMultiDisburse)).getResourceId();
|
||||||
|
LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general",
|
||||||
|
resourceId, isMultiDisburse);
|
||||||
|
return resourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PostLoanProductsRequest loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(boolean isMultiDisburse) {
|
||||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||||
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation();
|
AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation();
|
||||||
Long resourceId = loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
return new PostLoanProductsRequest().name(name).shortName(shortName).multiDisburseLoan(isMultiDisburse)
|
||||||
.multiDisburseLoan(isMultiDisburse).maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1)
|
.maxTrancheCount(isMultiDisburse ? 2 : 1).interestType(isMultiDisburse ? 0 : 1)
|
||||||
.interestCalculationPeriodType(isMultiDisburse ? 0 : 1).disallowExpectedDisbursements(isMultiDisburse)
|
.interestCalculationPeriodType(isMultiDisburse ? 0 : 1).disallowExpectedDisbursements(isMultiDisburse)
|
||||||
.description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1)
|
.description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1)
|
||||||
.recalculationRestFrequencyType(1).rescheduleStrategyMethod(1).loanScheduleType(LoanScheduleType.PROGRESSIVE.name())
|
.recalculationRestFrequencyType(1).rescheduleStrategyMethod(1).loanScheduleType(LoanScheduleType.PROGRESSIVE.name())
|
||||||
.recalculationRestFrequencyInterval(0).isInterestRecalculationEnabled(false).locale("en_GB").numberOfRepayments(4)
|
.recalculationRestFrequencyInterval(0).locale("en_GB").numberOfRepayments(4).repaymentFrequencyType(2L).repaymentEvery(1)
|
||||||
.repaymentFrequencyType(2L).repaymentEvery(1).minPrincipal(100.0).principal(1000.0).maxPrincipal(10000000.0)
|
.minPrincipal(100.0).principal(1000.0).maxPrincipal(10000000.0).amortizationType(1).interestRatePerPeriod(0.0)
|
||||||
.amortizationType(1).interestRatePerPeriod(0.0).interestRateFrequencyType(1).dateFormat("dd MMMM yyyy")
|
.interestRateFrequencyType(1).dateFormat("dd MMMM yyyy")
|
||||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).paymentAllocation(List.of(defaultAllocation))
|
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).paymentAllocation(List.of(defaultAllocation))
|
||||||
.accountingRule(3).enableAccrualActivityPosting(true).fundSourceAccountId(fundSource.getAccountID().longValue())//
|
.accountingRule(3).isInterestRecalculationEnabled(false).enableAccrualActivityPosting(true)
|
||||||
|
.fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||||
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||||
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
||||||
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
||||||
@@ -1663,18 +1707,30 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||||
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
|
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
|
||||||
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||||
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue());//
|
||||||
).getResourceId();
|
}
|
||||||
|
|
||||||
|
private Long createLoanProductAccountingAccrualPeriodicAdvancedPaymentAllocationInterestRecalculation(boolean isMultiDisburse) {
|
||||||
|
Long resourceId = loanTransactionHelper
|
||||||
|
.createLoanProduct(loanProductAccountingAccrualAdvanvedPaymentAllocationAccrualActivity(isMultiDisburse) //
|
||||||
|
.interestRatePerPeriod(10.0) //
|
||||||
|
.isInterestRecalculationEnabled(true)//
|
||||||
|
.preClosureInterestCalculationStrategy(1) // TILL_PRE_CLOSE_DATE
|
||||||
|
.rescheduleStrategyMethod(4) // ADJUST_LAST_UNPAID_PERIOD
|
||||||
|
.interestRecalculationCompoundingMethod(0) // NONE
|
||||||
|
.recalculationRestFrequencyType(2) // DAILY
|
||||||
|
.recalculationRestFrequencyInterval(1)//
|
||||||
|
).getResourceId();
|
||||||
LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general",
|
LOG.info("Test Progressive Loan Product Id {} isMultiDisburse {} http://localhost:4200/#/products/loan-products/{1}/general",
|
||||||
resourceId, isMultiDisburse);
|
resourceId, isMultiDisburse);
|
||||||
return resourceId;
|
return resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
||||||
BigDecimal principal, String applicationDisbursementDate) {
|
BigDecimal principal, String applicationDisbursementDate, BigDecimal interestRatePerPeriod) {
|
||||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||||
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO)
|
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(interestRatePerPeriod)
|
||||||
.interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy")
|
.interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy")
|
||||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
||||||
.expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate).clientId(clientID)
|
.expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate).clientId(clientID)
|
||||||
@@ -1686,10 +1742,11 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
private static Long applyForLoanApplicationAdvancedPaymentAllocation(final Long clientID, final Long loanProductID,
|
||||||
BigDecimal principal, String applicationDisbursementDate, String applicationDisbursementDate2) {
|
BigDecimal principal, String applicationDisbursementDate, String applicationDisbursementDate2,
|
||||||
|
BigDecimal interestRatePerPeriod) {
|
||||||
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||||
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||||
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(BigDecimal.ZERO)
|
.repaymentEvery(1).principal(principal).amortizationType(1).interestType(0).interestRatePerPeriod(interestRatePerPeriod)
|
||||||
.interestCalculationPeriodType(0).dateFormat("dd MMMM yyyy")
|
.interestCalculationPeriodType(0).dateFormat("dd MMMM yyyy")
|
||||||
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).loanType("individual")
|
||||||
.submittedOnDate(applicationDisbursementDate).clientId(clientID).expectedDisbursementDate(applicationDisbursementDate2)
|
.submittedOnDate(applicationDisbursementDate).clientId(clientID).expectedDisbursementDate(applicationDisbursementDate2)
|
||||||
|
|||||||
Reference in New Issue
Block a user