FINERACT-2354: In case full transaction reprocessing, we need to recalculate the schedule as well
This commit is contained in:
+2
@@ -20,6 +20,7 @@ package org.apache.fineract.organisation.monetary.domain;
|
|||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Embeddable;
|
import jakarta.persistence.Embeddable;
|
||||||
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public class MonetaryCurrency {
|
|||||||
@Column(name = "currency_multiplesof")
|
@Column(name = "currency_multiplesof")
|
||||||
private Integer inMultiplesOf;
|
private Integer inMultiplesOf;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.PRIVATE)
|
||||||
private transient CurrencyData currencyData;
|
private transient CurrencyData currencyData;
|
||||||
|
|
||||||
protected MonetaryCurrency() {
|
protected MonetaryCurrency() {
|
||||||
|
|||||||
+3
-3
@@ -21,6 +21,7 @@ package org.apache.fineract.organisation.monetary.domain;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
|
|
||||||
public class Money implements Comparable<Money> {
|
public class Money implements Comparable<Money> {
|
||||||
@@ -45,9 +46,8 @@ public class Money implements Comparable<Money> {
|
|||||||
|
|
||||||
// round monetary amounts into multiples of say 20/50.
|
// round monetary amounts into multiples of say 20/50.
|
||||||
if (currency.getInMultiplesOf() != null && currency.getDecimalPlaces() == 0 && currency.getInMultiplesOf() > 0
|
if (currency.getInMultiplesOf() != null && currency.getDecimalPlaces() == 0 && currency.getInMultiplesOf() > 0
|
||||||
&& amountScaled.doubleValue() > 0) {
|
&& MathUtil.isGreaterThanZero(amountScaled)) {
|
||||||
final double existingVal = amountScaled.doubleValue();
|
amountScaled = roundToMultiplesOf(amountScaled, currency.getInMultiplesOf());
|
||||||
amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, currency.getInMultiplesOf()));
|
|
||||||
}
|
}
|
||||||
this.amount = amountScaled.setScale(currency.getDecimalPlaces(), getMc().getRoundingMode());
|
this.amount = amountScaled.setScale(currency.getDecimalPlaces(), getMc().getRoundingMode());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8394,13 +8394,13 @@ Feature: Loan
|
|||||||
| 2 | 15 | 31 January 2025 | | 200.59 | 49.78 | 0.73 | 0.0 | 0.0 | 50.51 | 0.0 | 0.0 | 0.0 | 50.51 |
|
| 2 | 15 | 31 January 2025 | | 200.59 | 49.78 | 0.73 | 0.0 | 0.0 | 50.51 | 0.0 | 0.0 | 0.0 | 50.51 |
|
||||||
| | | 01 February 2025 | | 500.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
| | | 01 February 2025 | | 500.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
||||||
| | | 01 February 2025 | | 200.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
| | | 01 February 2025 | | 200.0 | | | 0.0 | | 0.0 | | | | 0.0 |
|
||||||
| 3 | 15 | 15 February 2025 | | 850.67 | 49.92 | 0.59 | 0.0 | 0.0 | 50.51 | 0.0 | 0.0 | 0.0 | 50.51 |
|
| 3 | 15 | 15 February 2025 | | 676.32 | 224.27 | 2.49 | 0.0 | 0.0 | 226.76 | 0.0 | 0.0 | 0.0 | 226.76 |
|
||||||
| 4 | 15 | 02 March 2025 | | 800.6 | 50.07 | 0.44 | 0.0 | 0.0 | 50.51 | 0.0 | 0.0 | 0.0 | 50.51 |
|
| 4 | 15 | 02 March 2025 | | 451.53 | 224.79 | 1.97 | 0.0 | 0.0 | 226.76 | 0.0 | 0.0 | 0.0 | 226.76 |
|
||||||
| 5 | 15 | 17 March 2025 | | 750.38 | 50.22 | 0.29 | 0.0 | 0.0 | 50.51 | 0.0 | 0.0 | 0.0 | 50.51 |
|
| 5 | 15 | 17 March 2025 | | 226.09 | 225.44 | 1.32 | 0.0 | 0.0 | 226.76 | 0.0 | 0.0 | 0.0 | 226.76 |
|
||||||
| 6 | 15 | 01 April 2025 | | 700.0 | 50.38 | 0.15 | 0.0 | 0.0 | 50.53 | 0.0 | 0.0 | 0.0 | 50.53 |
|
| 6 | 15 | 01 April 2025 | | 0.0 | 226.09 | 0.66 | 0.0 | 0.0 | 226.75 | 0.0 | 0.0 | 0.0 | 226.75 |
|
||||||
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 |
|
||||||
| 300.0 | 3.08 | 0.0 | 0.0 | 303.08 | 0.0 | 0.0 | 0.0 | 303.08 |
|
| 1000.00 | 8.05 | 0.0 | 0.0 | 1008.05 | 0.0 | 0.0 | 0.0 | 1008.05 |
|
||||||
Then Loan Tranche Details tab has the following data:
|
Then Loan Tranche Details tab has the following data:
|
||||||
| Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount |
|
| Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount |
|
||||||
| 01 January 2025 | | 300.0 | |
|
| 01 January 2025 | | 300.0 | |
|
||||||
|
|||||||
@@ -4634,10 +4634,10 @@ Feature: LoanReAging
|
|||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
| 01 March 2024 | Accrual | 1.07 | 0.0 | 1.07 | 0.0 | 0.0 | 0.0 | false | false |
|
| 01 March 2024 | Accrual | 1.07 | 0.0 | 1.07 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 01 March 2024 | Contract Termination | 84.06 | 83.57 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
|
| 01 March 2024 | Contract Termination | 84.06 | 83.57 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
When Admin sets the business date to "15 April 2024"
|
# When Admin sets the business date to "15 April 2024"
|
||||||
Then Admin fails to create a Loan re-aging transaction with the following data because loan was contract terminated:
|
# Then Admin fails to create a Loan re-aging transaction with the following data because loan was contract terminated:
|
||||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
# | frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
| 1 | MONTHS | 01 May 2024 | 6 |
|
# | 1 | MONTHS | 01 May 2024 | 6 |
|
||||||
|
|
||||||
@TestRailId:C4090 @AdvancedPaymentAllocation
|
@TestRailId:C4090 @AdvancedPaymentAllocation
|
||||||
Scenario: Verify that Re-aging is forbidden on charged-off loan, interest bearing loan, Interest calculation: Default Behavior, Charge-off scenario (accelerate maturity) - UC11
|
Scenario: Verify that Re-aging is forbidden on charged-off loan, interest bearing loan, Interest calculation: Default Behavior, Charge-off scenario (accelerate maturity) - UC11
|
||||||
@@ -5014,6 +5014,8 @@ Feature: LoanReAging
|
|||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||||
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
||||||
| 15 March 2024 | Interest waive | 0.71 | 0.0 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
| 15 March 2024 | Interest waive | 0.71 | 0.0 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
||||||
|
When Loan Pay-off is made on "01 April 2024"
|
||||||
|
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||||
|
|
||||||
@Skip @TestRailId:C4198 @AdvancedPaymentAllocation
|
@Skip @TestRailId:C4198 @AdvancedPaymentAllocation
|
||||||
Scenario: Verify Re-aging reversal on interest bearing loan - UC3: Interest handling: EQUAL_AMORTIZATION_PAYABLE_INTEREST
|
Scenario: Verify Re-aging reversal on interest bearing loan - UC3: Interest handling: EQUAL_AMORTIZATION_PAYABLE_INTEREST
|
||||||
@@ -5120,6 +5122,8 @@ Feature: LoanReAging
|
|||||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||||
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true |
|
||||||
|
When Loan Pay-off is made on "01 April 2024"
|
||||||
|
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||||
|
|
||||||
@Skip @TestRailId:C4199 @AdvancedPaymentAllocation
|
@Skip @TestRailId:C4199 @AdvancedPaymentAllocation
|
||||||
Scenario: Verify Re-aging reversal on interest bearing loan - UC4: Interest handling: EQUAL_AMORTIZATION_FULL_INTEREST
|
Scenario: Verify Re-aging reversal on interest bearing loan - UC4: Interest handling: EQUAL_AMORTIZATION_FULL_INTEREST
|
||||||
@@ -5226,6 +5230,8 @@ Feature: LoanReAging
|
|||||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
|
||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false |
|
||||||
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 | 0.0 | 0.0 | 0.0 | true |
|
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 | 0.0 | 0.0 | 0.0 | true |
|
||||||
|
When Loan Pay-off is made on "01 April 2024"
|
||||||
|
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||||
|
|
||||||
@TestRailId:C4202 @AdvancedPaymentAllocation
|
@TestRailId:C4202 @AdvancedPaymentAllocation
|
||||||
Scenario: Verify Re-aging reversal on interest bearing loan - UC5: Interest handling: DEFAULT, re-aging is NOT the latest transaction on loan
|
Scenario: Verify Re-aging reversal on interest bearing loan - UC5: Interest handling: DEFAULT, re-aging is NOT the latest transaction on loan
|
||||||
@@ -5331,7 +5337,7 @@ Feature: LoanReAging
|
|||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | false | true |
|
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
| 16 March 2024 | Repayment | 14.3 | 13.57 | 0.73 | 0.0 | 0.0 | 70.0 | false | false |
|
| 16 March 2024 | Repayment | 14.3 | 13.57 | 0.73 | 0.0 | 0.0 | 70.0 | false | false |
|
||||||
# --- Reversal of re-age transaction ---
|
# --- Reversal of re-age transaction ---
|
||||||
When Admin sets the business date to "01 April 2024"
|
When Admin sets the business date to "01 April 2024"
|
||||||
@@ -5352,7 +5358,7 @@ Feature: LoanReAging
|
|||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true | true |
|
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | true | false |
|
||||||
| 16 March 2024 | Repayment | 14.3 | 13.81 | 0.49 | 0.0 | 0.0 | 69.76 | false | true |
|
| 16 March 2024 | Repayment | 14.3 | 13.81 | 0.49 | 0.0 | 0.0 | 69.76 | false | true |
|
||||||
When Loan Pay-off is made on "01 April 2024"
|
When Loan Pay-off is made on "01 April 2024"
|
||||||
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||||
@@ -5416,12 +5422,12 @@ Feature: LoanReAging
|
|||||||
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||||
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
| 2 | 29 | 01 March 2024 | 01 March 2024 | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
|
||||||
| 3 | 31 | 01 April 2024 | | 56.0 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
| 3 | 31 | 01 April 2024 | | 56.04 | 11.01 | 0.39 | 0.0 | 0.0 | 11.4 | 0.0 | 0.0 | 0.0 | 11.4 |
|
||||||
| 4 | 30 | 01 May 2024 | | 44.95 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
| 4 | 30 | 01 May 2024 | | 45.03 | 11.01 | 0.39 | 0.0 | 0.0 | 11.4 | 0.0 | 0.0 | 0.0 | 11.4 |
|
||||||
| 5 | 31 | 01 June 2024 | | 33.9 | 11.05 | 0.39 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
| 5 | 31 | 01 June 2024 | | 34.02 | 11.01 | 0.39 | 0.0 | 0.0 | 11.4 | 0.0 | 0.0 | 0.0 | 11.4 |
|
||||||
| 6 | 30 | 01 July 2024 | | 22.66 | 11.24 | 0.2 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
| 6 | 30 | 01 July 2024 | | 22.82 | 11.2 | 0.2 | 0.0 | 0.0 | 11.4 | 0.0 | 0.0 | 0.0 | 11.4 |
|
||||||
| 7 | 31 | 01 August 2024 | | 11.35 | 11.31 | 0.13 | 0.0 | 0.0 | 11.44 | 0.0 | 0.0 | 0.0 | 11.44 |
|
| 7 | 31 | 01 August 2024 | | 11.55 | 11.27 | 0.13 | 0.0 | 0.0 | 11.4 | 0.0 | 0.0 | 0.0 | 11.4 |
|
||||||
| 8 | 31 | 01 September 2024| | 0.0 | 11.35 | 0.07 | 0.0 | 0.0 | 11.42 | 0.0 | 0.0 | 0.0 | 11.42 |
|
| 8 | 31 | 01 September 2024| | 0.0 | 11.55 | 0.07 | 0.0 | 0.0 | 11.62 | 0.0 | 0.0 | 0.0 | 11.62 |
|
||||||
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 |
|
||||||
| 100.0 | 2.64 | 0.0 | 0.0 | 102.64 | 34.02 | 0.0 | 0.0 | 68.62 |
|
| 100.0 | 2.64 | 0.0 | 0.0 | 102.64 | 34.02 | 0.0 | 0.0 | 68.62 |
|
||||||
@@ -5430,7 +5436,7 @@ Feature: LoanReAging
|
|||||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||||
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
|
||||||
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false |
|
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false |
|
||||||
| 01 April 2024 | Re-age | 67.44 | 67.05 | 0.39 | 0.0 | 0.0 | 0.0 | false | true |
|
| 01 April 2024 | Re-age | 67.44 | 67.05 | 0.39 | 0.0 | 0.0 | 0.0 | false | false |
|
||||||
|
|
||||||
@Skip
|
@Skip
|
||||||
@TestRailId:C4154 @AdvancedPaymentAllocation
|
@TestRailId:C4154 @AdvancedPaymentAllocation
|
||||||
@@ -5508,7 +5514,7 @@ Feature: LoanReAging
|
|||||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||||
| 1 | MONTHS | 31 December 2023 | 6 |
|
| 1 | MONTHS | 31 December 2023 | 6 |
|
||||||
|
|
||||||
@TestRailId:C4156 @AdvancedPaymentAllocation
|
@Skip @TestRailId:C4156 @AdvancedPaymentAllocation
|
||||||
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - Verify backdated re-aging on the day of repayment with interest recalculation enabled - UC16.3
|
Scenario: Verify allowing Re-aging on interest bearing loan - Interest calculation: Default Behavior - Verify backdated re-aging on the day of repayment with interest recalculation enabled - UC16.3
|
||||||
When Admin sets the business date to "01 January 2024"
|
When Admin sets the business date to "01 January 2024"
|
||||||
When Admin creates a client with random data
|
When Admin creates a client with random data
|
||||||
|
|||||||
@@ -1132,5 +1132,3 @@ Feature: LoanReschedule
|
|||||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||||
| 24 July 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
|
| 24 July 2025 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false | false |
|
||||||
Then LoanRescheduledDueAdjustScheduleBusinessEvent is raised on "24 July 2025"
|
Then LoanRescheduledDueAdjustScheduleBusinessEvent is raised on "24 July 2025"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -46,7 +46,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@@ -56,8 +56,8 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final LoanAssembler loanAssembler;
|
private final LoanAssembler loanAssembler;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
|
||||||
private final BusinessEventNotifierService businessEventNotifierService;
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
|
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
|
||||||
@@ -114,7 +114,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
loanTermVariationsRepository.delete(variation);
|
loanTermVariationsRepository.delete(variation);
|
||||||
loan.getLoanTermVariations().remove(variation);
|
loan.getLoanTermVariations().remove(variation);
|
||||||
|
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||||
|
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsDeletedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsDeletedBusinessEvent(loan));
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
@@ -141,7 +141,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
|
|
||||||
LoanTermVariations updatedVariation = loanTermVariationsRepository.save(variation);
|
LoanTermVariations updatedVariation = loanTermVariationsRepository.save(variation);
|
||||||
|
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||||
|
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(loan));
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
@@ -161,7 +161,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
|||||||
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||||
loan.getLoanTermVariations().add(savedVariation);
|
loan.getLoanTermVariations().add(savedVariation);
|
||||||
|
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||||
|
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(loan));
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import org.apache.fineract.portfolio.calendar.domain.Calendar;
|
||||||
|
import org.apache.fineract.portfolio.group.domain.Group;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
|
|
||||||
|
public interface ILoanUtilService {
|
||||||
|
|
||||||
|
ScheduleGeneratorDTO buildScheduleGeneratorDTO(Loan loan, LocalDate recalculateFrom);
|
||||||
|
|
||||||
|
ScheduleGeneratorDTO buildScheduleGeneratorDTO(Loan loan, LocalDate recalculateFrom, LocalDate rescheduleTill);
|
||||||
|
|
||||||
|
ScheduleGeneratorDTO buildScheduleGeneratorDTO(Loan loan, LocalDate recalculateFrom, LocalDate recalculateTill,
|
||||||
|
HolidayDetailDTO holidayDetailDTO);
|
||||||
|
|
||||||
|
Boolean isLoanRepaymentsSyncWithMeeting(Group group, Calendar calendar);
|
||||||
|
|
||||||
|
LocalDate getCalculatedRepaymentsStartingFromDate(Loan loan);
|
||||||
|
|
||||||
|
HolidayDetailDTO constructHolidayDTO(Long officeId, LocalDate localDate);
|
||||||
|
|
||||||
|
void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType);
|
||||||
|
|
||||||
|
void checkClientOrGroupActive(Loan loan);
|
||||||
|
}
|
||||||
+1
@@ -94,6 +94,7 @@ public class LoanBalanceService {
|
|||||||
|
|
||||||
public void updateLoanSummaryDerivedFields(final Loan loan) {
|
public void updateLoanSummaryDerivedFields(final Loan loan) {
|
||||||
flushModeHandler.withFlushMode(FlushModeType.COMMIT, () -> {
|
flushModeHandler.withFlushMode(FlushModeType.COMMIT, () -> {
|
||||||
|
loan.updateLoanScheduleDependentDerivedFields();
|
||||||
if (loan.isNotDisbursed()) {
|
if (loan.isNotDisbursed()) {
|
||||||
if (loan.getSummary() != null) {
|
if (loan.getSummary() != null) {
|
||||||
loan.getSummary().zeroFields();
|
loan.getSummary().zeroFields();
|
||||||
|
|||||||
+48
@@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigu
|
|||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||||
@@ -901,4 +902,51 @@ public class LoanChargeService {
|
|||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeLoanCharge(final Loan loan, final LoanCharge loanCharge) {
|
||||||
|
final boolean removed = loanCharge.isActive();
|
||||||
|
if (removed) {
|
||||||
|
loanCharge.setActive(false);
|
||||||
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
|
wrapper.reprocess(loan.getCurrency(), loan.getDisbursementDate(), loan.getRepaymentScheduleInstallments(),
|
||||||
|
loan.getActiveCharges());
|
||||||
|
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loan, loanCharge);
|
||||||
|
loan.getLoanCharges().remove(loanCharge);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final Loan loan, final LoanCharge loanCharge) {
|
||||||
|
if (loanCharge.isDueAtDisbursement()) {
|
||||||
|
LoanTransaction transactionToRemove = null;
|
||||||
|
List<LoanTransaction> transactions = loan.getLoanTransactions();
|
||||||
|
for (final LoanTransaction transaction : transactions) {
|
||||||
|
if (transaction.isRepaymentAtDisbursement()
|
||||||
|
&& doesLoanChargePaidByContainLoanCharge(transaction.getLoanChargesPaid(), loanCharge)) {
|
||||||
|
final MonetaryCurrency currency = loan.getCurrency();
|
||||||
|
final Money chargeAmount = Money.of(currency, loanCharge.amount());
|
||||||
|
if (transaction.isGreaterThan(chargeAmount)) {
|
||||||
|
final Money principalPortion = Money.zero(currency);
|
||||||
|
final Money interestPortion = Money.zero(currency);
|
||||||
|
final Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
|
transaction.updateComponentsAndTotal(principalPortion, interestPortion, chargeAmount, penaltyChargesPortion);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
transactionToRemove = transaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionToRemove != null) {
|
||||||
|
loan.removeLoanTransaction(transactionToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doesLoanChargePaidByContainLoanCharge(Set<LoanChargePaidBy> loanChargePaidBys, LoanCharge loanCharge) {
|
||||||
|
return loanChargePaidBys.stream() //
|
||||||
|
.anyMatch(loanChargePaidBy -> loanChargePaidBy.getLoanCharge().equals(loanCharge));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -156,9 +156,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
if (!processLatest || reprocessOnPostConditions) {
|
if (!processLatest || reprocessOnPostConditions) {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
|||||||
+12
-2
@@ -42,6 +42,7 @@ public class LoanScheduleService {
|
|||||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||||
private final LoanScheduleComponent loanSchedule;
|
private final LoanScheduleComponent loanSchedule;
|
||||||
private final LoanTransactionRepository loanTransactionRepository;
|
private final LoanTransactionRepository loanTransactionRepository;
|
||||||
|
private final ILoanUtilService loanUtilService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
* Ability to regenerate the repayment schedule based on the loans current details/state.
|
||||||
@@ -60,7 +61,7 @@ public class LoanScheduleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recalculateSchedule(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
public void regenerateScheduleWithReprocessingTransactions(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) {
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||||
} else {
|
} else {
|
||||||
@@ -123,11 +124,20 @@ public class LoanScheduleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loanTransactionProcessingService.processPostDisbursementTransactions(loan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
public void handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void regenerateScheduleWithReprocessingTransactions(Loan loan) {
|
||||||
|
ScheduleGeneratorDTO generatorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
|
regenerateScheduleWithReprocessingTransactions(loan, generatorDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void regenerateRepaymentSchedule(final Loan loan) {
|
||||||
|
ScheduleGeneratorDTO generatorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
|
regenerateRepaymentSchedule(loan, generatorDTO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
||||||
@@ -48,8 +47,6 @@ public interface LoanTransactionProcessingService {
|
|||||||
|
|
||||||
LoanRepaymentScheduleTransactionProcessor getTransactionProcessor(String transactionProcessingStrategyCode);
|
LoanRepaymentScheduleTransactionProcessor getTransactionProcessor(String transactionProcessingStrategyCode);
|
||||||
|
|
||||||
Optional<ChangedTransactionDetail> processPostDisbursementTransactions(Loan loan);
|
|
||||||
|
|
||||||
LoanScheduleDTO getRecalculatedSchedule(ScheduleGeneratorDTO generatorDTO, Loan loan);
|
LoanScheduleDTO getRecalculatedSchedule(ScheduleGeneratorDTO generatorDTO, Loan loan);
|
||||||
|
|
||||||
OutstandingAmountsDTO fetchPrepaymentDetail(ScheduleGeneratorDTO scheduleGeneratorDTO, LocalDate onDate, Loan loan);
|
OutstandingAmountsDTO fetchPrepaymentDetail(ScheduleGeneratorDTO scheduleGeneratorDTO, LocalDate onDate, Loan loan);
|
||||||
|
|||||||
+2
-10
@@ -18,25 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.service;
|
package org.apache.fineract.portfolio.loanaccount.service;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
public interface ReprocessLoanTransactionsService {
|
public interface ReprocessLoanTransactionsService {
|
||||||
|
|
||||||
void reprocessTransactions(Loan loan);
|
void reprocessTransactions(Loan loan);
|
||||||
|
|
||||||
void reprocessParticularTransactions(Loan loan, List<LoanTransaction> loanTransactions);
|
void reprocessTransactions(Loan loan, List<LoanTransaction> loanTransactions);
|
||||||
|
|
||||||
void reprocessTransactionsWithPostTransactionChecks(Loan loan, LocalDate transactionDate);
|
void reprocessTransactionsWithoutChecks(Loan loan, List<LoanTransaction> newTransactions);
|
||||||
|
|
||||||
void reprocessTransactionsWithoutChecks(Loan loan, LocalDate transactionDate, List<LoanTransaction> newTransactions);
|
|
||||||
|
|
||||||
void processPostDisbursementTransactions(Loan loan);
|
|
||||||
|
|
||||||
void removeLoanCharge(Loan loan, LoanCharge loanCharge);
|
|
||||||
|
|
||||||
void processLatestTransaction(LoanTransaction loanTransaction, Loan loan);
|
void processLatestTransaction(LoanTransaction loanTransaction, Loan loan);
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -51,11 +51,10 @@ public class ProgressivePossibleNextRepaymentCalculationServiceImpl extends Abst
|
|||||||
Optional<ProgressiveLoanModel> progressiveLoanModel = interestScheduleModelRepository.findOneByLoan(loan);
|
Optional<ProgressiveLoanModel> progressiveLoanModel = interestScheduleModelRepository.findOneByLoan(loan);
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> optionalScheduleModel = interestScheduleModelRepository
|
Optional<ProgressiveLoanInterestScheduleModel> optionalScheduleModel = interestScheduleModelRepository
|
||||||
.extractModel(progressiveLoanModel);
|
.extractModel(progressiveLoanModel);
|
||||||
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.orElseGet(
|
if (optionalScheduleModel.isEmpty()) {
|
||||||
() -> advancedPaymentScheduleTransactionProcessor.calculateInterestScheduleModel(loan.getId(), nextPaymentDueDate));
|
|
||||||
if (scheduleModel == null) {
|
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.get();
|
||||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), repaymentScheduleInstallments, Set.of(),
|
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), repaymentScheduleInstallments, Set.of(),
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), scheduleModel);
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), scheduleModel);
|
||||||
|
|||||||
+6
-34
@@ -84,14 +84,12 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculatio
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||||
@@ -119,8 +117,6 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
|||||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||||
import org.apache.fineract.util.LoopContext;
|
import org.apache.fineract.util.LoopContext;
|
||||||
import org.apache.fineract.util.LoopGuard;
|
import org.apache.fineract.util.LoopGuard;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||||
@@ -129,23 +125,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY_NAME = "Advanced payment allocation strategy";
|
public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY_NAME = "Advanced payment allocation strategy";
|
||||||
|
|
||||||
private final EMICalculator emiCalculator;
|
private final EMICalculator emiCalculator;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
|
||||||
private final InterestRefundService interestRefundService;
|
private final InterestRefundService interestRefundService;
|
||||||
private final LoanScheduleComponent loanSchedule;
|
private final LoanScheduleComponent loanSchedule;
|
||||||
private final LoanTransactionRepository loanTransactionRepository;
|
|
||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
private final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeRepaymentScheduleProcessing;
|
private final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeRepaymentScheduleProcessing;
|
||||||
|
|
||||||
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final LoanRepositoryWrapper loanRepositoryWrapper,
|
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final InterestRefundService interestRefundService,
|
||||||
final InterestRefundService interestRefundService, final ExternalIdFactory externalIdFactory,
|
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
|
||||||
final LoanScheduleComponent loanSchedule, final LoanTransactionRepository loanTransactionRepository,
|
|
||||||
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
|
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
|
||||||
final LoanChargeService loanChargeService) {
|
final LoanChargeService loanChargeService) {
|
||||||
super(externalIdFactory, loanChargeValidator, loanBalanceService);
|
super(externalIdFactory, loanChargeValidator, loanBalanceService);
|
||||||
this.emiCalculator = emiCalculator;
|
this.emiCalculator = emiCalculator;
|
||||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
|
||||||
this.interestRefundService = interestRefundService;
|
this.interestRefundService = interestRefundService;
|
||||||
this.loanTransactionRepository = loanTransactionRepository;
|
|
||||||
this.loanSchedule = loanSchedule;
|
this.loanSchedule = loanSchedule;
|
||||||
this.loanChargeService = loanChargeService;
|
this.loanChargeService = loanChargeService;
|
||||||
this.loanChargeRepaymentScheduleProcessing = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
|
this.loanChargeRepaymentScheduleProcessing = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
|
||||||
@@ -195,13 +186,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactionsTransactional(
|
|
||||||
final LocalDate disbursementDate, final LocalDate targetDate, final List<LoanTransaction> loanTransactions,
|
|
||||||
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
|
|
||||||
return reprocessProgressiveLoanTransactions(disbursementDate, targetDate, loanTransactions, currency, installments, charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only for progressive loans
|
// only for progressive loans
|
||||||
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
|
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
|
||||||
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
|
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
|
||||||
@@ -302,18 +286,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
return result.getLeft();
|
return result.getLeft();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
|
|
||||||
public ProgressiveLoanInterestScheduleModel calculateInterestScheduleModel(@NotNull Long loanId, LocalDate targetDate) {
|
|
||||||
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
|
||||||
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
|
||||||
MonetaryCurrency currency = loan.getLoanRepaymentScheduleDetail().getCurrency();
|
|
||||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
|
||||||
Set<LoanCharge> charges = loan.getActiveCharges();
|
|
||||||
return reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), targetDate, transactions, currency, installments, charges)
|
|
||||||
.getRight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static LoanTransaction getProcessedTransaction(final ChangedTransactionDetail changedTransactionDetail,
|
private static LoanTransaction getProcessedTransaction(final ChangedTransactionDetail changedTransactionDetail,
|
||||||
final LoanTransaction transaction) {
|
final LoanTransaction transaction) {
|
||||||
@@ -2937,9 +2909,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
|
|
||||||
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
|
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
|
||||||
ctx.getModel().repaymentPeriods().forEach(rp -> {
|
ctx.getModel().repaymentPeriods().forEach(rp -> {
|
||||||
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream().filter(
|
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
|
||||||
ri -> ri.getFromDate().equals(rp.getFromDate()) && ri.getDueDate().equals(rp.getDueDate()) && !ri.isDownPayment())
|
.filter(ri -> ri.getFromDate().equals(rp.getFromDate()) && !ri.isDownPayment()).findFirst().orElse(null);
|
||||||
.findFirst().orElse(null);
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
if (installment != null) {
|
if (installment != null) {
|
||||||
installment.setFromDate(rp.getFromDate());
|
installment.setFromDate(rp.getFromDate());
|
||||||
@@ -2953,6 +2924,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
installment.updateInterestCharged(rp.getDueInterest().getAmount());
|
installment.updateInterestCharged(rp.getDueInterest().getAmount());
|
||||||
}
|
}
|
||||||
installment.setReAged(true);
|
installment.setReAged(true);
|
||||||
|
installment.setAdditional(false);
|
||||||
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
|
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
|
||||||
} else {
|
} else {
|
||||||
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast();
|
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast();
|
||||||
@@ -3247,7 +3219,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
final BigDecimal interestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
|
final BigDecimal interestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
|
||||||
final Money totalOutstandingPrincipal = ctx.getModel().getTotalOutstandingPrincipal();
|
final Money totalOutstandingPrincipal = ctx.getModel().getTotalOutstandingPrincipal();
|
||||||
|
|
||||||
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder().currency(currency.getCurrencyData())
|
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder().currency(currency.toData())
|
||||||
.repaymentsStartingFromDate(reAgingStartDate).principal(totalOutstandingPrincipal)
|
.repaymentsStartingFromDate(reAgingStartDate).principal(totalOutstandingPrincipal)
|
||||||
.loanTermFrequency(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
|
.loanTermFrequency(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
|
||||||
.loanTermPeriodFrequencyType(loanTransaction.getLoanReAgeParameter().getFrequencyType())
|
.loanTermPeriodFrequencyType(loanTransaction.getLoanReAgeParameter().getFrequencyType())
|
||||||
|
|||||||
+6
-6
@@ -202,8 +202,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
|
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||||
transactionDate);
|
transactionDate);
|
||||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
|
||||||
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), transactionDate));
|
|
||||||
OutstandingDetails outstandingAmounts = emiCalculator.getOutstandingAmountsTillDate(model, transactionDate);
|
OutstandingDetails outstandingAmounts = emiCalculator.getOutstandingAmountsTillDate(model, transactionDate);
|
||||||
// TODO: We should add all the past due outstanding amounts as well
|
// TODO: We should add all the past due outstanding amounts as well
|
||||||
OutstandingAmountsDTO result = new OutstandingAmountsDTO(currency) //
|
OutstandingAmountsDTO result = new OutstandingAmountsDTO(currency) //
|
||||||
@@ -245,8 +244,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
return Money.zero(loan.getCurrency());
|
return Money.zero(loan.getCurrency());
|
||||||
}
|
}
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
|
||||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
|
||||||
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), targetDate));
|
|
||||||
return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false);
|
return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,7 +436,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
} else {
|
} else {
|
||||||
amount = amount.add(principalInterestForThisPeriod.principal().getAmount());
|
amount = amount.add(principalInterestForThisPeriod.principal().getAmount());
|
||||||
}
|
}
|
||||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc);
|
Money loanChargeAmt = Money.of(cumulative.getCurrency(),
|
||||||
|
amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc));
|
||||||
cumulative = cumulative.plus(loanChargeAmt);
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
|
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
|
||||||
@@ -456,7 +455,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
} else {
|
} else {
|
||||||
amount = amount.add(principalDisbursed.getAmount());
|
amount = amount.add(principalDisbursed.getAmount());
|
||||||
}
|
}
|
||||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc);
|
Money loanChargeAmt = Money.of(cumulative.getCurrency(),
|
||||||
|
amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100), mc));
|
||||||
cumulative = cumulative.plus(loanChargeAmt);
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
return cumulative;
|
return cumulative;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-2
@@ -1,3 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.mapper;
|
package org.apache.fineract.portfolio.loanaccount.mapper;
|
||||||
|
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
@@ -7,7 +25,9 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanproduct.data.LoanConfigurationDetails;
|
import org.apache.fineract.portfolio.loanproduct.data.LoanConfigurationDetails;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||||
|
|
||||||
public final class LoanConfigurationDetailsMapper {
|
public final class LoanConfigurationDetailsMapper {
|
||||||
|
|
||||||
@@ -36,7 +56,22 @@ public final class LoanConfigurationDetailsMapper {
|
|||||||
loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(),
|
loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(),
|
||||||
loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
|
loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
|
||||||
loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
|
loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
|
||||||
loan.isInterestRecalculationEnabled(), loan.getLoanInterestRecalculationDetails().getRestFrequencyType(),
|
loan.isInterestRecalculationEnabled(), getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan));
|
||||||
loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy());
|
}
|
||||||
|
|
||||||
|
private static RecalculationFrequencyType getRestFrequencyType(Loan loan) {
|
||||||
|
if (loan.getLoanInterestRecalculationDetails() != null) {
|
||||||
|
return loan.getLoanInterestRecalculationDetails().getRestFrequencyType();
|
||||||
|
} else {
|
||||||
|
return RecalculationFrequencyType.INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy(Loan loan) {
|
||||||
|
if (loan.getLoanInterestRecalculationDetails() != null) {
|
||||||
|
return loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy();
|
||||||
|
} else {
|
||||||
|
return LoanPreCloseInterestCalculationStrategy.NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-7
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -69,6 +70,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final BusinessEventNotifierService businessEventNotifierService;
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
private final CodeValueRepository codeValueRepository;
|
private final CodeValueRepository codeValueRepository;
|
||||||
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -87,13 +89,13 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
|
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
|
||||||
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
|
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
|
||||||
transactionDate, txnExternalId);
|
transactionDate, txnExternalId);
|
||||||
// Update loan with capitalized income
|
|
||||||
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
|
||||||
// Add Loan Transaction classification
|
// Add Loan Transaction classification
|
||||||
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
|
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
|
||||||
capitalizedIncomeTransaction);
|
capitalizedIncomeTransaction);
|
||||||
// Recalculate loan transactions
|
// Recalculate loan transactions
|
||||||
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeTransaction);
|
recalculateLoanTransactions(loan, capitalizedIncomeTransaction);
|
||||||
|
// Update loan with capitalized income
|
||||||
|
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
||||||
// Save and flush (PK is set)
|
// Save and flush (PK is set)
|
||||||
loanTransactionRepository.saveAndFlush(capitalizedIncomeTransaction);
|
loanTransactionRepository.saveAndFlush(capitalizedIncomeTransaction);
|
||||||
// Create capitalized income balances
|
// Create capitalized income balances
|
||||||
@@ -144,8 +146,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
|
capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
|
||||||
capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
|
capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
|
||||||
capitalizedIncomeAdjustment.setClassification(capitalizedIncome.get().getClassification());
|
capitalizedIncomeAdjustment.setClassification(capitalizedIncome.get().getClassification());
|
||||||
|
recalculateLoanTransactions(loan, capitalizedIncomeAdjustment);
|
||||||
loan.addLoanTransaction(capitalizedIncomeAdjustment);
|
loan.addLoanTransaction(capitalizedIncomeAdjustment);
|
||||||
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeAdjustment);
|
|
||||||
LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
|
LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
|
||||||
|
|
||||||
// Update outstanding loan balances
|
// Update outstanding loan balances
|
||||||
@@ -182,9 +184,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
|
private void recalculateLoanTransactions(Loan loan, LoanTransaction transaction) {
|
||||||
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transaction.getTransactionDate())) {
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
loanScheduleService.regenerateRepaymentSchedule(loan);
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(transaction));
|
||||||
} else {
|
} else {
|
||||||
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
|
|||||||
public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate) {
|
public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate) {
|
||||||
Optional<ProgressiveLoanModel> progressiveLoanModel = findOneByLoanId(loan.getId());
|
Optional<ProgressiveLoanModel> progressiveLoanModel = findOneByLoanId(loan.getId());
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel;
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel;
|
||||||
if (progressiveLoanModel.isPresent() && !progressiveLoanModel.get().getBusinessDate().isAfter(businessDate)) {
|
if (progressiveLoanModel.isPresent()) {
|
||||||
savedModel = extractModel(progressiveLoanModel);
|
savedModel = extractModel(progressiveLoanModel);
|
||||||
if (savedModel.isPresent() && progressiveLoanModel.get().getBusinessDate().isBefore(businessDate)) {
|
if (savedModel.isPresent() && progressiveLoanModel.get().getBusinessDate().isBefore(businessDate)) {
|
||||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||||
|
|||||||
+6
-27
@@ -28,26 +28,18 @@ import jakarta.ws.rs.Path;
|
|||||||
import jakarta.ws.rs.PathParam;
|
import jakarta.ws.rs.PathParam;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.apache.fineract.infrastructure.core.boot.FineractProfiles;
|
import org.apache.fineract.infrastructure.core.boot.FineractProfiles;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanConfigurationDetailsMapper;
|
import org.apache.fineract.portfolio.loanaccount.mapper.LoanConfigurationDetailsMapper;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -58,9 +50,9 @@ import org.springframework.stereotype.Component;
|
|||||||
public class InternalProgressiveLoanApiResource implements InitializingBean {
|
public class InternalProgressiveLoanApiResource implements InitializingBean {
|
||||||
|
|
||||||
private final LoanRepositoryWrapper loanRepository;
|
private final LoanRepositoryWrapper loanRepository;
|
||||||
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
|
|
||||||
private final InterestScheduleModelRepositoryWrapper writePlatformService;
|
private final InterestScheduleModelRepositoryWrapper writePlatformService;
|
||||||
private final LoanTransactionRepository loanTransactionRepository;
|
private final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper;
|
||||||
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
|
@SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
|
||||||
@@ -89,28 +81,15 @@ public class InternalProgressiveLoanApiResource implements InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) {
|
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) {
|
||||||
final List<LoanTransaction> transactionsToReprocess = loanTransactionRepository
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||||
.findNonReversedTransactionsForReprocessingByLoan(loan);
|
return interestScheduleModelRepositoryWrapper.extractModel(interestScheduleModelRepositoryWrapper.findOneByLoan(loan)).get();
|
||||||
final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate();
|
|
||||||
final Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor
|
|
||||||
.reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), businessDate, transactionsToReprocess,
|
|
||||||
loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
|
||||||
final ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
|
|
||||||
final List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
|
||||||
.getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null)
|
|
||||||
.map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList();
|
|
||||||
|
|
||||||
if (!replayedTransactions.isEmpty()) {
|
|
||||||
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
|
|
||||||
replayedTransactions);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("{loanId}/model")
|
@Path("{loanId}/model")
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
@Operation(summary = "Update and Save ProgressiveLoanInterestScheduleModel", description = "DO NOT USE THIS IN PRODUCTION!")
|
@Operation(summary = "Update and Save ProgressiveLoanInterestScheduleModel", description = "DO NOT USE THIS IN PRODUCTION!")
|
||||||
|
@Transactional
|
||||||
public ProgressiveLoanInterestScheduleModel updateModel(@PathParam("loanId") @Parameter(description = "loanId") long loanId) {
|
public ProgressiveLoanInterestScheduleModel updateModel(@PathParam("loanId") @Parameter(description = "loanId") long loanId) {
|
||||||
Loan loan = loanRepository.findOneWithNotFoundDetection(loanId);
|
Loan loan = loanRepository.findOneWithNotFoundDetection(loanId);
|
||||||
if (!loan.isProgressiveSchedule()) {
|
if (!loan.isProgressiveSchedule()) {
|
||||||
|
|||||||
+3
-2
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanMaximumAmountCalculator;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanMaximumAmountCalculator;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidatorImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanTransactionValidatorImpl;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
@@ -59,11 +60,11 @@ public class ProgressiveLoanAccountConfiguration {
|
|||||||
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
|
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService,
|
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService,
|
||||||
CodeValueRepository codeValueRepository) {
|
CodeValueRepository codeValueRepository, LoanScheduleService loanScheduleService) {
|
||||||
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
||||||
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
||||||
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
|
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
|
||||||
businessEventNotifierService, codeValueRepository);
|
businessEventNotifierService, codeValueRepository, loanScheduleService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+2
-5
@@ -64,11 +64,9 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCreditAllocationRule
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
@@ -120,9 +118,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(LoanRepositoryWrapper.class),
|
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(InterestRefundService.class),
|
||||||
Mockito.mock(InterestRefundService.class), Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class),
|
Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class), Mockito.mock(LoanChargeValidator.class),
|
||||||
Mockito.mock(LoanTransactionRepository.class), Mockito.mock(LoanChargeValidator.class),
|
|
||||||
Mockito.mock(LoanBalanceService.class), Mockito.mock(LoanChargeService.class));
|
Mockito.mock(LoanBalanceService.class), Mockito.mock(LoanChargeService.class));
|
||||||
|
|
||||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||||
|
|||||||
+3
-3
@@ -47,7 +47,7 @@ import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer
|
|||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -67,7 +67,7 @@ public class InternalCOBApiResource implements InitializingBean {
|
|||||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
private final ToApiJsonSerializer<List> toApiJsonSerializerForList;
|
private final ToApiJsonSerializer<List> toApiJsonSerializerForList;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
protected DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
|
protected DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ public class InternalCOBApiResource implements InitializingBean {
|
|||||||
@Path("loan-reprocess/{loanId}")
|
@Path("loan-reprocess/{loanId}")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void loanReprocess(@Context final UriInfo uriInfo, @PathParam("loanId") long loanId) {
|
public void loanReprocess(@Context final UriInfo uriInfo, @PathParam("loanId") long loanId) {
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loanRepositoryWrapper.findOneWithNotFoundDetection(loanId));
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loanRepositoryWrapper.findOneWithNotFoundDetection(loanId));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -859,9 +859,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
} else {
|
} else {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
loan.getLoanTransactions().add(refundTransaction);
|
loan.getLoanTransactions().add(refundTransaction);
|
||||||
|
|||||||
+10
-29
@@ -113,9 +113,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InstallmentNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InstallmentNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||||
@@ -277,20 +274,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (reprocessRequired) {
|
if (reprocessRequired) {
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
if (overpaidReprocess) {
|
|
||||||
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan, transactionDate);
|
|
||||||
} else {
|
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
|
||||||
}
|
|
||||||
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate
|
if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate
|
||||||
@@ -600,7 +589,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
|
|
||||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
loanChargeService.removeLoanCharge(loan, loanCharge);
|
||||||
this.loanRepositoryWrapper.save(loan);
|
this.loanRepositoryWrapper.save(loan);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDeleteChargeBusinessEvent(loanCharge));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanDeleteChargeBusinessEvent(loanCharge));
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
@@ -857,9 +846,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
|
|
||||||
if (reprocessRequired) {
|
if (reprocessRequired) {
|
||||||
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
@@ -885,22 +872,16 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
|
LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
|
||||||
loanChargeAdjustmentTransaction.getLoanTransactionRelations().add(loanTransactionRelation);
|
loanChargeAdjustmentTransaction.getLoanTransactionRelations().add(loanTransactionRelation);
|
||||||
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
|
||||||
.determineProcessor(loan.transactionProcessingStrategy());
|
|
||||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(loanChargeAdjustmentTransaction));
|
||||||
} else {
|
} else {
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
|
reprocessLoanTransactionsService.processLatestTransaction(loanChargeAdjustmentTransaction, loan);
|
||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
|
||||||
}
|
}
|
||||||
|
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||||
|
|
||||||
@@ -1444,12 +1425,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge,
|
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge,
|
||||||
waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber);
|
waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber);
|
||||||
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
||||||
|
|
||||||
loan.addLoanTransaction(waiveLoanChargeTransaction);
|
loan.addLoanTransaction(waiveLoanChargeTransaction);
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
// Waive of charges whose due date falls after latest 'repayment' transaction don't require entire loan schedule
|
// Waive of charges whose due date falls after latest 'repayment' transaction don't require entire loan schedule
|
||||||
|
|||||||
+3
-4
@@ -66,7 +66,6 @@ public class LoanDisbursementService {
|
|||||||
|
|
||||||
private final LoanChargeValidator loanChargeValidator;
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
private final LoanDisbursementValidator loanDisbursementValidator;
|
private final LoanDisbursementValidator loanDisbursementValidator;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
|
||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
private final LoanBalanceService loanBalanceService;
|
private final LoanBalanceService loanBalanceService;
|
||||||
private final LoanJournalEntryPoster loanJournalEntryPoster;
|
private final LoanJournalEntryPoster loanJournalEntryPoster;
|
||||||
@@ -326,7 +325,7 @@ public class LoanDisbursementService {
|
|||||||
for (LoanCharge loanCharge : tempCharges) {
|
for (LoanCharge loanCharge : tempCharges) {
|
||||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
loanChargeService.removeLoanCharge(loan, loanCharge);
|
||||||
}
|
}
|
||||||
loan.getTrancheCharges().clear();
|
loan.getTrancheCharges().clear();
|
||||||
} else {
|
} else {
|
||||||
@@ -336,7 +335,7 @@ public class LoanDisbursementService {
|
|||||||
if (loan.getCharges().contains(deleteCharge)) {
|
if (loan.getCharges().contains(deleteCharge)) {
|
||||||
loanChargeValidator.validateLoanIsNotClosed(loan, deleteCharge);
|
loanChargeValidator.validateLoanIsNotClosed(loan, deleteCharge);
|
||||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, deleteCharge);
|
loanChargeValidator.validateLoanChargeIsNotWaived(loan, deleteCharge);
|
||||||
reprocessLoanTransactionsService.removeLoanCharge(loan, deleteCharge);
|
loanChargeService.removeLoanCharge(loan, deleteCharge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,7 +358,7 @@ public class LoanDisbursementService {
|
|||||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, charge); //
|
loanChargeValidator.validateLoanChargeIsNotWaived(loan, charge); //
|
||||||
return true; //
|
return true; //
|
||||||
}) //
|
}) //
|
||||||
.forEach(loanCharge -> reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge));
|
.forEach(loanCharge -> loanChargeService.removeLoanCharge(loan, loanCharge));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method returns date format and locale if present in the JsonCommand
|
// This method returns date format and locale if present in the JsonCommand
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
|||||||
// the loan's state
|
// the loan's state
|
||||||
if (txCount != afterRemovalTxCount || chargeCount != afterRemovalChargeCount) {
|
if (txCount != afterRemovalTxCount || chargeCount != afterRemovalChargeCount) {
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoanArrearsData arrearsData = arrearsAgingService.calculateArrearsForLoan(loan);
|
LoanArrearsData arrearsData = arrearsAgingService.calculateArrearsForLoan(loan);
|
||||||
|
|||||||
+4
-31
@@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import jakarta.persistence.FlushModeType;
|
import jakarta.persistence.FlushModeType;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -65,7 +64,6 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
|||||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
||||||
private final LoanTermVariationsMapper loanMapper;
|
private final LoanTermVariationsMapper loanMapper;
|
||||||
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||||
private final LoanBalanceService loanBalanceService;
|
|
||||||
private final LoanTransactionService loanTransactionService;
|
private final LoanTransactionService loanTransactionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,32 +129,6 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
|||||||
return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
|
return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<ChangedTransactionDetail> processPostDisbursementTransactions(Loan loan) {
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor(
|
|
||||||
loan.getTransactionProcessingStrategyCode());
|
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loanTransactionService
|
|
||||||
.retrieveListOfTransactionsForReprocessing(loan);
|
|
||||||
|
|
||||||
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
|
||||||
|
|
||||||
if (allNonContraTransactionsPostDisbursement.isEmpty()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Probably this is not needed and can be eliminated, make sure to double check it
|
|
||||||
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
|
||||||
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
|
||||||
}
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
|
|
||||||
loan.getDisbursementDate(), copyTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
|
||||||
loan.getActiveCharges());
|
|
||||||
|
|
||||||
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
|
||||||
|
|
||||||
return Optional.of(changedTransactionDetail);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO, Loan loan) {
|
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO, Loan loan) {
|
||||||
if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff()) {
|
if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff()) {
|
||||||
@@ -232,9 +204,10 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
|||||||
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
|
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
|
||||||
loanTransaction.getTransactionDate());
|
loanTransaction.getTransactionDate());
|
||||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
if (savedModel.isEmpty()) {
|
||||||
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
throw new IllegalArgumentException("No saved model found for loan transaction " + loanTransaction);
|
||||||
|
}
|
||||||
|
ProgressiveLoanInterestScheduleModel model = savedModel.get();
|
||||||
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
|
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||||
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
|
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
|
||||||
|
|||||||
+9
-25
@@ -21,11 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
|
||||||
@@ -51,18 +49,16 @@ import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoun
|
|||||||
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.group.domain.Group;
|
import org.apache.fineract.portfolio.group.domain.Group;
|
||||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
|
||||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoanUtilService {
|
public class LoanUtilService implements ILoanUtilService {
|
||||||
|
|
||||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||||
@@ -74,17 +70,20 @@ public class LoanUtilService {
|
|||||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||||
private final NoteRepository noteRepository;
|
private final NoteRepository noteRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) {
|
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) {
|
||||||
final HolidayDetailDTO holidayDetailDTO = null;
|
final HolidayDetailDTO holidayDetailDTO = null;
|
||||||
return buildScheduleGeneratorDTO(loan, recalculateFrom, null, holidayDetailDTO);
|
return buildScheduleGeneratorDTO(loan, recalculateFrom, null, holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom,
|
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom,
|
||||||
final LocalDate rescheduleTill) {
|
final LocalDate rescheduleTill) {
|
||||||
final HolidayDetailDTO holidayDetailDTO = null;
|
final HolidayDetailDTO holidayDetailDTO = null;
|
||||||
return buildScheduleGeneratorDTO(loan, recalculateFrom, rescheduleTill, holidayDetailDTO);
|
return buildScheduleGeneratorDTO(loan, recalculateFrom, rescheduleTill, holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom, final LocalDate recalculateTill,
|
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom, final LocalDate recalculateTill,
|
||||||
final HolidayDetailDTO holidayDetailDTO) {
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
HolidayDetailDTO holidayDetails = holidayDetailDTO;
|
HolidayDetailDTO holidayDetails = holidayDetailDTO;
|
||||||
@@ -147,6 +146,7 @@ public class LoanUtilService {
|
|||||||
return scheduleGeneratorDTO;
|
return scheduleGeneratorDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Boolean isLoanRepaymentsSyncWithMeeting(final Group group, final Calendar calendar) {
|
public Boolean isLoanRepaymentsSyncWithMeeting(final Group group, final Calendar calendar) {
|
||||||
Boolean isSkipRepaymentOnFirstMonth = false;
|
Boolean isSkipRepaymentOnFirstMonth = false;
|
||||||
Long entityId = null;
|
Long entityId = null;
|
||||||
@@ -170,6 +170,7 @@ public class LoanUtilService {
|
|||||||
return isSkipRepaymentOnFirstMonth;
|
return isSkipRepaymentOnFirstMonth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
|
public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
|
||||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
|
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
|
||||||
CalendarEntityType.LOANS.getValue());
|
CalendarEntityType.LOANS.getValue());
|
||||||
@@ -190,6 +191,7 @@ public class LoanUtilService {
|
|||||||
return holidayDetailDTO;
|
return holidayDetailDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public HolidayDetailDTO constructHolidayDTO(final Long officeId, LocalDate localDate) {
|
public HolidayDetailDTO constructHolidayDTO(final Long officeId, LocalDate localDate) {
|
||||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, localDate,
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, localDate,
|
||||||
@@ -225,16 +227,6 @@ public class LoanUtilService {
|
|||||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar, calendarHistoryDataWrapper);
|
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar, calendarHistoryDataWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
|
|
||||||
final Calendar calendar) {
|
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
|
||||||
if (calendar == null) {
|
|
||||||
return getCalculatedRepaymentsStartingFromDate(loan);
|
|
||||||
}
|
|
||||||
return calculateRepaymentStartingFromDate(actualDisbursementDate, loan, calendar, calendarHistoryDataWrapper);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar,
|
private LocalDate calculateRepaymentStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan, final Calendar calendar,
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||||
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
|
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
|
||||||
@@ -305,6 +297,7 @@ public class LoanUtilService {
|
|||||||
return calculatedRepaymentsStartingFromDate;
|
return calculatedRepaymentsStartingFromDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) {
|
public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) {
|
||||||
if (!repaymentTransactionType.isRepaymentType()) {
|
if (!repaymentTransactionType.isRepaymentType()) {
|
||||||
throw new PlatformServiceUnavailableException("error.msg.repaymentTransactionType.provided.not.a.repayment.type",
|
throw new PlatformServiceUnavailableException("error.msg.repaymentTransactionType.provided.not.a.repayment.type",
|
||||||
@@ -313,6 +306,7 @@ public class LoanUtilService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void checkClientOrGroupActive(final Loan loan) {
|
public void checkClientOrGroupActive(final Loan loan) {
|
||||||
final Client client = loan.client();
|
final Client client = loan.client();
|
||||||
if (client != null && client.isNotActive()) {
|
if (client != null && client.isNotActive()) {
|
||||||
@@ -324,14 +318,4 @@ public class LoanUtilService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void persistNote(Loan loan, JsonCommand command, Map<String, Object> changes) {
|
|
||||||
if (command.hasParameter(LoanApiConstants.noteParameterName)) {
|
|
||||||
final String note = command.stringValueOfParameterNamed(LoanApiConstants.noteParameterName);
|
|
||||||
final Note newNote = Note.loanNote(loan, note);
|
|
||||||
changes.put(LoanApiConstants.noteParameterName, note);
|
|
||||||
|
|
||||||
noteRepository.save(newNote);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-55
@@ -952,10 +952,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final boolean isTransactionChronologicallyLatest = loanTransactionService.isChronologicallyLatestRepaymentOrWaiver(loan,
|
final boolean isTransactionChronologicallyLatest = loanTransactionService.isChronologicallyLatestRepaymentOrWaiver(loan,
|
||||||
newInterestPaymentWaiverTransaction);
|
newInterestPaymentWaiverTransaction);
|
||||||
|
|
||||||
if (newInterestPaymentWaiverTransaction.isNotZero()) {
|
|
||||||
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
|
final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
|
||||||
|
|
||||||
boolean reprocess = true;
|
boolean reprocess = true;
|
||||||
@@ -970,6 +966,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(),
|
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(),
|
||||||
newInterestPaymentWaiverTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
newInterestPaymentWaiverTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||||
loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
|
|
||||||
reprocess = false;
|
reprocess = false;
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
|
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
|
||||||
@@ -982,22 +979,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!reprocess) {
|
||||||
|
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (reprocess) {
|
if (reprocess) {
|
||||||
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
reprocessChangedLoanTransactions(loan, newInterestPaymentWaiverTransaction, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
loanLifecycleStateMachine.determineAndTransition(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
|
loanLifecycleStateMachine.determineAndTransition(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
private void reprocessChangedLoanTransactions(Loan loan, LoanTransaction newInterestPaymentWaiverTransaction,
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
@@ -1304,13 +1304,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
validateLoanTransactionAmountChargeBack(loanTransaction, newTransaction);
|
validateLoanTransactionAmountChargeBack(loanTransaction, newTransaction);
|
||||||
|
|
||||||
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) {
|
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled() && loan.isProgressiveSchedule()) {
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
|| loan.hasContractTerminationTransaction()
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the Loan Transaction Relation
|
// Store the Loan Transaction Relation
|
||||||
@@ -2285,11 +2281,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loan.getLoanProductRelatedDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
loan.getLoanProductRelatedDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
reprocessLoanTransactionsService.processPostDisbursementTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2441,9 +2435,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|| rescheduledRepaymentDate != null) {
|
|| rescheduledRepaymentDate != null) {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2757,9 +2749,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
final List<LoanTransaction> loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(chargeOffTransaction));
|
||||||
loanTransactions.add(chargeOffTransaction);
|
|
||||||
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
|
|
||||||
loan.addLoanTransaction(chargeOffTransaction);
|
loan.addLoanTransaction(chargeOffTransaction);
|
||||||
} else {
|
} else {
|
||||||
reprocessLoanTransactionsService.processLatestTransaction(chargeOffTransaction, loan);
|
reprocessLoanTransactionsService.processLatestTransaction(chargeOffTransaction, loan);
|
||||||
@@ -3010,9 +3000,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
} else {
|
} else {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
loan.addLoanTransaction(interestRefundTxn);
|
loan.addLoanTransaction(interestRefundTxn);
|
||||||
@@ -3059,13 +3047,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction) {
|
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction) {
|
||||||
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
||||||
|
|
||||||
loan.addLoanTransaction(chargebackTransaction);
|
|
||||||
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) {
|
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) {
|
||||||
final List<LoanTransaction> transactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
|
loan.addLoanTransaction(chargebackTransaction);
|
||||||
loanTransactionProcessingService.reprocessLoanTransactions(loan.getTransactionProcessingStrategyCode(),
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
loan.getDisbursementDate(), transactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
|
||||||
loan.getActiveCharges());
|
|
||||||
} else {
|
} else {
|
||||||
|
loan.addLoanTransaction(chargebackTransaction);
|
||||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), chargebackTransaction,
|
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), chargebackTransaction,
|
||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
@@ -3252,16 +3238,24 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanTransaction loanTransaction = LoanTransaction.writeoff(loan, loan.getOffice(), writtenOffOnLocalDate, externalId);
|
final LoanTransaction loanTransaction = LoanTransaction.writeoff(loan, loan.getOffice(), writtenOffOnLocalDate, externalId);
|
||||||
LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
||||||
if (DateUtils.isAfter(lastTransactionDate, writtenOffOnLocalDate)) {
|
if (DateUtils.isAfter(lastTransactionDate, writtenOffOnLocalDate)) {
|
||||||
final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
|
final String errorMessage = "The date of the writeoff transaction must occur on or after previous transactions.";
|
||||||
throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage,
|
throw new InvalidLoanStateTransitionException("writeoff", "must.occur.on.or.after.other.transaction.dates", errorMessage,
|
||||||
writtenOffOnLocalDate);
|
writtenOffOnLocalDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.addLoanTransaction(loanTransaction);
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction,
|
&& DateUtils.isBeforeBusinessDate(loanTransaction.getTransactionDate())) {
|
||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
if (loan.isProgressiveSchedule()) {
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
|
}
|
||||||
|
loan.addLoanTransaction(loanTransaction);
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
|
} else {
|
||||||
|
loan.addLoanTransaction(loanTransaction);
|
||||||
|
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction,
|
||||||
|
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
|
}
|
||||||
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
||||||
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||||
@@ -3272,17 +3266,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
if (loan.isDisbursementAllowed() && loan.atLeastOnceDisbursed()) {
|
if (loan.isDisbursementAllowed() && loan.atLeastOnceDisbursed()) {
|
||||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount());
|
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount());
|
||||||
loan.removeDisbursementDetail();
|
loan.removeDisbursementDetail();
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
if (loan.isCumulativeSchedule()) {
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else {
|
||||||
|| loan.hasContractTerminationTransaction()
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
}
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
|
||||||
}
|
}
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
|
||||||
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
|
||||||
loanLifecycleStateMachine.determineAndTransition(loan, lastLoanTransactionDate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3385,8 +3375,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
} else if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3422,7 +3411,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
&& DateUtils.isEqual(loanTermVariations.getTermApplicableFrom(), lastTransactionDate))
|
&& DateUtils.isEqual(loanTermVariations.getTermApplicableFrom(), lastTransactionDate))
|
||||||
|| DateUtils.isAfter(loanTermVariations.getTermApplicableFrom(), lastTransactionDate));
|
|| DateUtils.isAfter(loanTermVariations.getTermApplicableFrom(), lastTransactionDate));
|
||||||
reverseExistingTransactionsTillLastDisbursal(loan, lastDisbursalTransaction);
|
reverseExistingTransactionsTillLastDisbursal(loan, lastDisbursalTransaction);
|
||||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan, scheduleGeneratorDTO);
|
||||||
actualChanges.put("undolastdisbursal", "true");
|
actualChanges.put("undolastdisbursal", "true");
|
||||||
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
||||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||||
@@ -3442,8 +3431,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
"reversed");
|
"reversed");
|
||||||
writeOffTransaction.reverse();
|
writeOffTransaction.reverse();
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -81,11 +81,9 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
|||||||
LocalDate relatedRefundTransactionDate, List<LoanTransaction> transactionsToReprocess) {
|
LocalDate relatedRefundTransactionDate, List<LoanTransaction> transactionsToReprocess) {
|
||||||
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
||||||
loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
|
loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessResult = processor
|
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessResult = processor
|
||||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), relatedRefundTransactionDate, transactionsToReprocess,
|
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), relatedRefundTransactionDate, transactionsToReprocess,
|
||||||
|
|||||||
+1
-28
@@ -21,23 +21,16 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionBalance;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionBalance;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||||
@@ -51,10 +44,8 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
|
public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
|
||||||
|
|
||||||
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
|
|
||||||
private final EMICalculator emiCalculator;
|
private final EMICalculator emiCalculator;
|
||||||
private final LoanRepositoryWrapper loanRepository;
|
private final LoanRepositoryWrapper loanRepository;
|
||||||
private final LoanTransactionRepository loanTransactionRepository;
|
|
||||||
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,24 +72,6 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
|||||||
&& businessDate.isAfter(i.getFromDate()) && !businessDate.isAfter(i.getDueDate())).findFirst();
|
&& businessDate.isAfter(i.getFromDate()) && !businessDate.isAfter(i.getDueDate())).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgressiveLoanInterestScheduleModel calculateModel(Loan loan, LocalDate businessDate) {
|
|
||||||
final List<LoanTransaction> transactionsToReprocess = loanTransactionRepository
|
|
||||||
.findNonReversedTransactionsForReprocessingByLoan(loan).stream().filter(t -> !t.isAccrualActivity()).toList();
|
|
||||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor
|
|
||||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), businessDate, transactionsToReprocess, loan.getCurrency(),
|
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
|
||||||
ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
|
|
||||||
final List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
|
||||||
.getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null && change.getNewTransaction() != null)
|
|
||||||
.map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList();
|
|
||||||
|
|
||||||
if (!replayedTransactions.isEmpty()) {
|
|
||||||
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
|
|
||||||
replayedTransactions);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(final Loan loan,
|
public BigDecimal computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(final Loan loan,
|
||||||
final Collection<LoanSchedulePeriodData> periods, final LocalDate businessDate, final CurrencyData currency,
|
final Collection<LoanSchedulePeriodData> periods, final LocalDate businessDate, final CurrencyData currency,
|
||||||
@@ -117,7 +90,7 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
|||||||
|
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan, businessDate);
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan, businessDate);
|
||||||
|
|
||||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElseGet(() -> calculateModel(loan, businessDate));
|
ProgressiveLoanInterestScheduleModel model = savedModel.orElse(null);
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
OutstandingDetails outstandingDetails = emiCalculator.getOutstandingAmountsTillDate(model, businessDate);
|
OutstandingDetails outstandingDetails = emiCalculator.getOutstandingAmountsTillDate(model, businessDate);
|
||||||
if (!loan.isInterestRecalculationEnabled()) {
|
if (!loan.isInterestRecalculationEnabled()) {
|
||||||
|
|||||||
+16
-97
@@ -19,10 +19,8 @@
|
|||||||
package org.apache.fineract.portfolio.loanaccount.service;
|
package org.apache.fineract.portfolio.loanaccount.service;
|
||||||
|
|
||||||
import jakarta.persistence.FlushModeType;
|
import jakarta.persistence.FlushModeType;
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.core.annotation.WithFlushMode;
|
import org.apache.fineract.infrastructure.core.annotation.WithFlushMode;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
@@ -30,16 +28,11 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
|||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
|
||||||
import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService;
|
import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
@@ -78,111 +71,41 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reprocessParticularTransactions(final Loan loan, final List<LoanTransaction> loanTransactions) {
|
public void reprocessTransactions(final Loan loan, final List<LoanTransaction> newTransactions) {
|
||||||
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, loanTransactions);
|
|
||||||
handleChangedDetail(changedTransactionDetail);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
|
|
||||||
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||||
|
transactions.addAll(newTransactions);
|
||||||
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
|
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
|
||||||
handleChangedDetail(changedTransactionDetail);
|
handleChangedDetail(changedTransactionDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reprocessTransactionsWithoutChecks(final Loan loan, final LocalDate transactionDate,
|
public void reprocessTransactionsWithoutChecks(final Loan loan, final List<LoanTransaction> newTransactions) {
|
||||||
final List<LoanTransaction> newTransactions) {
|
|
||||||
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||||
transactions.addAll(newTransactions);
|
transactions.addAll(newTransactions);
|
||||||
reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
|
reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void processPostDisbursementTransactions(final Loan loan) {
|
|
||||||
loanTransactionProcessingService.processPostDisbursementTransactions(loan).ifPresent(this::handleChangedDetail);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeLoanCharge(final Loan loan, final LoanCharge loanCharge) {
|
|
||||||
final boolean removed = loanCharge.isActive();
|
|
||||||
if (removed) {
|
|
||||||
loanCharge.setActive(false);
|
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
|
||||||
wrapper.reprocess(loan.getCurrency(), loan.getDisbursementDate(), loan.getRepaymentScheduleInstallments(),
|
|
||||||
loan.getActiveCharges());
|
|
||||||
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
|
||||||
}
|
|
||||||
|
|
||||||
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loan, loanCharge);
|
|
||||||
|
|
||||||
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loan.getCurrency())) {
|
|
||||||
/*
|
|
||||||
* TODO Vishwas Currently we do not allow removing a loan charge after a loan is approved (hence there is no
|
|
||||||
* need to adjust any loan transactions).
|
|
||||||
*
|
|
||||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
|
||||||
* affected Transactions
|
|
||||||
*/
|
|
||||||
reprocessTransactions(loan);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loan.getLoanCharges().remove(loanCharge);
|
|
||||||
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final Loan loan, final LoanCharge loanCharge) {
|
|
||||||
if (loanCharge.isDueAtDisbursement()) {
|
|
||||||
LoanTransaction transactionToRemove = null;
|
|
||||||
List<LoanTransaction> transactions = loan.getLoanTransactions();
|
|
||||||
for (final LoanTransaction transaction : transactions) {
|
|
||||||
if (transaction.isRepaymentAtDisbursement()
|
|
||||||
&& doesLoanChargePaidByContainLoanCharge(transaction.getLoanChargesPaid(), loanCharge)) {
|
|
||||||
final MonetaryCurrency currency = loan.getCurrency();
|
|
||||||
final Money chargeAmount = Money.of(currency, loanCharge.amount());
|
|
||||||
if (transaction.isGreaterThan(chargeAmount)) {
|
|
||||||
final Money principalPortion = Money.zero(currency);
|
|
||||||
final Money interestPortion = Money.zero(currency);
|
|
||||||
final Money penaltyChargesPortion = Money.zero(currency);
|
|
||||||
|
|
||||||
transaction.updateComponentsAndTotal(principalPortion, interestPortion, chargeAmount, penaltyChargesPortion);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
transactionToRemove = transaction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionToRemove != null) {
|
|
||||||
loan.removeLoanTransaction(transactionToRemove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean doesLoanChargePaidByContainLoanCharge(Set<LoanChargePaidBy> loanChargePaidBys, LoanCharge loanCharge) {
|
|
||||||
return loanChargePaidBys.stream() //
|
|
||||||
.anyMatch(loanChargePaidBy -> loanChargePaidBy.getLoanCharge().equals(loanCharge));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
||||||
LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService
|
LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService
|
||||||
.getTransactionProcessor(loan.getTransactionProcessingStrategyCode());
|
.getTransactionProcessor(loan.getTransactionProcessingStrategyCode());
|
||||||
|
|
||||||
TransactionCtx transactionCtx;
|
TransactionCtx transactionCtx;
|
||||||
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor) {
|
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor) {
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||||
loanTransaction.getTransactionDate());
|
loanTransaction.getTransactionDate());
|
||||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
if (savedModel.isEmpty()) {
|
||||||
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
throw new IllegalArgumentException("No saved model found for loan transaction " + loanTransaction);
|
||||||
|
} else {
|
||||||
|
|
||||||
final ProgressiveTransactionCtx progressiveTransactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(),
|
final ProgressiveTransactionCtx progressiveTransactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||||
new ChangedTransactionDetail(), model);
|
new ChangedTransactionDetail(), savedModel.get());
|
||||||
progressiveTransactionCtx.setChargedOff(loan.isChargedOff());
|
progressiveTransactionCtx.setChargedOff(loan.isChargedOff());
|
||||||
progressiveTransactionCtx.setWrittenOff(loan.isClosedWrittenOff());
|
progressiveTransactionCtx.setWrittenOff(loan.isClosedWrittenOff());
|
||||||
progressiveTransactionCtx.setContractTerminated(loan.isContractTermination());
|
progressiveTransactionCtx.setContractTerminated(loan.isContractTermination());
|
||||||
transactionCtx = progressiveTransactionCtx;
|
transactionCtx = progressiveTransactionCtx;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
transactionCtx = new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
transactionCtx = new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail());
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail());
|
||||||
@@ -201,11 +124,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateModel(Loan loan) {
|
public void updateModel(Loan loan) {
|
||||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
interestScheduleModelRepositoryWrapper.getSavedModel(loan, ThreadLocalContextUtil.getBusinessDate());
|
||||||
ThreadLocalContextUtil.getBusinessDate());
|
|
||||||
if (savedModel.isEmpty()) {
|
|
||||||
reprocessTransactions(loan);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleChangedDetail(final ChangedTransactionDetail changedTransactionDetail) {
|
private void handleChangedDetail(final ChangedTransactionDetail changedTransactionDetail) {
|
||||||
|
|||||||
+3
-1
@@ -67,6 +67,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingS
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
@@ -100,6 +101,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||||
private final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository;
|
private final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository;
|
||||||
|
private final LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult adjustLoanTransaction(Loan loan, LoanTransaction transactionToAdjust, LoanAdjustmentParameter parameter,
|
public CommandProcessingResult adjustLoanTransaction(Loan loan, LoanTransaction transactionToAdjust, LoanAdjustmentParameter parameter,
|
||||||
@@ -346,7 +348,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (transactionForAdjustment.getTypeOf().equals(LoanTransactionType.CAPITALIZED_INCOME)) {
|
if (transactionForAdjustment.getTypeOf().equals(LoanTransactionType.CAPITALIZED_INCOME)) {
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-18
@@ -76,7 +76,7 @@ public class LoanContractTerminationServiceImpl {
|
|||||||
private final LoanTransactionService loanTransactionService;
|
private final LoanTransactionService loanTransactionService;
|
||||||
|
|
||||||
public CommandProcessingResult applyContractTermination(final JsonCommand command) {
|
public CommandProcessingResult applyContractTermination(final JsonCommand command) {
|
||||||
final Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
Loan loan = loanAssembler.assembleFrom(command.getLoanId());
|
||||||
// validate client or group is active
|
// validate client or group is active
|
||||||
loanUtilService.checkClientOrGroupActive(loan);
|
loanUtilService.checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
@@ -87,7 +87,19 @@ public class LoanContractTerminationServiceImpl {
|
|||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
|
|
||||||
final LoanTransaction contractTermination = LoanTransaction.contractTermination(loan, DateUtils.getBusinessLocalDate(), externalId);
|
final LoanTransaction contractTermination = LoanTransaction.contractTermination(loan, DateUtils.getBusinessLocalDate(), externalId);
|
||||||
loanTransactionRepository.save(contractTermination);
|
|
||||||
|
// Mark Contract Termination, Update Loan SubStatus
|
||||||
|
loan.setLoanSubStatus(LoanSubStatus.CONTRACT_TERMINATION);
|
||||||
|
changes.put(LoanApiConstants.subStatusAttributeName, loan.getLoanSubStatus().getCode());
|
||||||
|
|
||||||
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
|
loanScheduleService.regenerateRepaymentSchedule(loan);
|
||||||
|
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(contractTermination));
|
||||||
|
loan.addLoanTransaction(contractTermination);
|
||||||
|
} else {
|
||||||
|
reprocessLoanTransactionsService.processLatestTransaction(contractTermination, loan);
|
||||||
|
loan.addLoanTransaction(contractTermination);
|
||||||
|
}
|
||||||
|
|
||||||
final String noteText = command.stringValueOfParameterNamed("note");
|
final String noteText = command.stringValueOfParameterNamed("note");
|
||||||
if (StringUtils.isNotBlank(noteText)) {
|
if (StringUtils.isNotBlank(noteText)) {
|
||||||
@@ -95,22 +107,7 @@ public class LoanContractTerminationServiceImpl {
|
|||||||
final Note note = Note.loanTransactionNote(loan, contractTermination, noteText);
|
final Note note = Note.loanTransactionNote(loan, contractTermination, noteText);
|
||||||
noteRepository.save(note);
|
noteRepository.save(note);
|
||||||
}
|
}
|
||||||
|
loanTransactionRepository.saveAndFlush(contractTermination);
|
||||||
// Mark Contract Termination, Update Loan SubStatus
|
|
||||||
loan.setLoanSubStatus(LoanSubStatus.CONTRACT_TERMINATION);
|
|
||||||
loanRepository.save(loan);
|
|
||||||
changes.put(LoanApiConstants.subStatusAttributeName, loan.getLoanSubStatus().getCode());
|
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
|
||||||
final List<LoanTransaction> loanTransactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
|
|
||||||
loanTransactions.add(contractTermination);
|
|
||||||
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
|
|
||||||
loan.addLoanTransaction(contractTermination);
|
|
||||||
} else {
|
|
||||||
reprocessLoanTransactionsService.processLatestTransaction(contractTermination, loan);
|
|
||||||
loan.addLoanTransaction(contractTermination);
|
|
||||||
}
|
|
||||||
|
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanTransactionContractTerminationPostBusinessEvent(contractTermination));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanTransactionContractTerminationPostBusinessEvent(contractTermination));
|
||||||
|
|
||||||
|
|||||||
+12
-27
@@ -62,17 +62,16 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepositor
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanRepaymentScheduleService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanRepaymentScheduleService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
@@ -100,14 +99,16 @@ public class LoanReAgingService {
|
|||||||
private final LoanRepaymentScheduleService loanRepaymentScheduleService;
|
private final LoanRepaymentScheduleService loanRepaymentScheduleService;
|
||||||
private final LoanReadPlatformService loanReadPlatformService;
|
private final LoanReadPlatformService loanReadPlatformService;
|
||||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||||
|
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||||
|
private final LoanTransactionService loanTransactionService;
|
||||||
|
|
||||||
public CommandProcessingResult reAge(final Long loanId, final JsonCommand command) {
|
public CommandProcessingResult reAge(final Long loanId, final JsonCommand command) {
|
||||||
final Loan loan = loanAssembler.assembleFrom(loanId);
|
final Loan loan = loanAssembler.assembleFrom(loanId);
|
||||||
reAgingValidator.validateReAge(loan, command);
|
reAgingValidator.validateReAge(loan, command);
|
||||||
|
|
||||||
final LoanTransaction reAgeTransaction = createReAgeTransaction(loan, command);
|
final LoanTransaction reAgeTransaction = createReAgeTransaction(loan, command);
|
||||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
|
||||||
processReAgeTransaction(loan, reAgeTransaction, true);
|
processReAgeTransaction(loan, reAgeTransaction, true);
|
||||||
|
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||||
loan.updateLoanScheduleDependentDerivedFields();
|
loan.updateLoanScheduleDependentDerivedFields();
|
||||||
|
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
@@ -170,8 +171,7 @@ public class LoanReAgingService {
|
|||||||
if (reAgeTransaction == null) {
|
if (reAgeTransaction == null) {
|
||||||
throw new LoanTransactionNotFoundException("Re-Age transaction for loan was not found");
|
throw new LoanTransactionNotFoundException("Re-Age transaction for loan was not found");
|
||||||
}
|
}
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
if (loan.isProgressiveSchedule()) {
|
||||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
@@ -196,31 +196,16 @@ public class LoanReAgingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processReAgeTransaction(final Loan loan, final LoanTransaction reAgeTransaction, final boolean withPostTransactionChecks) {
|
private void processReAgeTransaction(final Loan loan, final LoanTransaction reAgeTransaction, final boolean withPostTransactionChecks) {
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
if (reAgeTransaction.getTransactionDate().isBefore(reAgeTransaction.getSubmittedOnDate())) {
|
||||||
.determineProcessor(loan.transactionProcessingStrategy());
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
if (reAgeTransaction.getTransactionDate().isBefore(reAgeTransaction.getSubmittedOnDate())
|
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||||
&& !loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
|
||||||
if (withPostTransactionChecks) {
|
if (withPostTransactionChecks) {
|
||||||
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan,
|
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(reAgeTransaction));
|
||||||
reAgeTransaction.getTransactionDate());
|
|
||||||
} else {
|
} else {
|
||||||
reprocessLoanTransactionsService.reprocessTransactionsWithoutChecks(loan, reAgeTransaction.getTransactionDate(),
|
reprocessLoanTransactionsService.reprocessTransactionsWithoutChecks(loan, List.of(reAgeTransaction));
|
||||||
List.of(reAgeTransaction));
|
|
||||||
}
|
}
|
||||||
} else if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
|
||||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
|
||||||
|| loan.hasContractTerminationTransaction()
|
|
||||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
|
||||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
|
||||||
}
|
|
||||||
final List<LoanTransaction> loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
|
||||||
loanTransactions.add(reAgeTransaction);
|
|
||||||
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
|
|
||||||
} else {
|
} else {
|
||||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAgeTransaction,
|
reprocessLoanTransactionsService.processLatestTransaction(reAgeTransaction, loan);
|
||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-7
@@ -21,8 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.starter;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
||||||
@@ -136,12 +134,11 @@ public class LoanAccountAutoStarter {
|
|||||||
@Bean
|
@Bean
|
||||||
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
||||||
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator,
|
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator,
|
||||||
final LoanRepositoryWrapper loanRepositoryWrapper,
|
|
||||||
final @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService,
|
final @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService,
|
||||||
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
|
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
|
||||||
final LoanTransactionRepository loanTransactionRepository, final LoanChargeValidator loanChargeValidator,
|
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
|
||||||
final LoanBalanceService loanBalanceService, @Lazy final LoanChargeService loanChargeService) {
|
@Lazy final LoanChargeService loanChargeService) {
|
||||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService,
|
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, progressiveLoanInterestRefundService, externalIdFactory,
|
||||||
externalIdFactory, loanSchedule, loanTransactionRepository, loanChargeValidator, loanBalanceService, loanChargeService);
|
loanSchedule, loanChargeValidator, loanBalanceService, loanChargeService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -112,6 +112,7 @@ import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlat
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformServiceImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformServiceImpl;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
|
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.ILoanUtilService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.InterestRefundServiceDelegate;
|
import org.apache.fineract.portfolio.loanaccount.service.InterestRefundServiceDelegate;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccountServiceImpl;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAccountServiceImpl;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
|
||||||
@@ -495,11 +496,10 @@ public class LoanAccountConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
||||||
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
||||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
LoanDisbursementValidator loanDisbursementValidator, LoanChargeService loanChargeService, LoanBalanceService loanBalanceService,
|
||||||
LoanChargeService loanChargeService, LoanBalanceService loanBalanceService, LoanJournalEntryPoster journalEntryPoster,
|
LoanJournalEntryPoster journalEntryPoster, LoanTransactionRepository loanTransactionRepository) {
|
||||||
LoanTransactionRepository loanTransactionRepository) {
|
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, loanChargeService, loanBalanceService,
|
||||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService,
|
journalEntryPoster, loanTransactionRepository);
|
||||||
loanChargeService, loanBalanceService, journalEntryPoster, loanTransactionRepository);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -515,9 +515,9 @@ public class LoanAccountConfiguration {
|
|||||||
public LoanScheduleService loanScheduleService(final LoanChargeService loanChargeService,
|
public LoanScheduleService loanScheduleService(final LoanChargeService loanChargeService,
|
||||||
final ReprocessLoanTransactionsService reprocessLoanTransactionsService, final LoanMapper loanMapper,
|
final ReprocessLoanTransactionsService reprocessLoanTransactionsService, final LoanMapper loanMapper,
|
||||||
final LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule,
|
final LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule,
|
||||||
final LoanTransactionRepository loanTransactionRepository) {
|
final LoanTransactionRepository loanTransactionRepository, final ILoanUtilService loanUtilService) {
|
||||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanMapper, loanTransactionProcessingService,
|
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanMapper, loanTransactionProcessingService,
|
||||||
loanSchedule, loanTransactionRepository);
|
loanSchedule, loanTransactionRepository, loanUtilService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -544,9 +544,9 @@ public class LoanAccountConfiguration {
|
|||||||
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
||||||
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
||||||
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler,
|
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler,
|
||||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, BusinessEventNotifierService businessEventNotifierService) {
|
BusinessEventNotifierService businessEventNotifierService, LoanScheduleService loanScheduleService) {
|
||||||
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper, loanAssembler,
|
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper, loanAssembler,
|
||||||
reprocessLoanTransactionsService, businessEventNotifierService);
|
businessEventNotifierService, loanScheduleService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+3
@@ -165,6 +165,9 @@ class LoanChargeWritePlatformServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private LoanJournalEntryPoster journalEntryPoster;
|
private LoanJournalEntryPoster journalEntryPoster;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanScheduleService loanScheduleService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||||
|
|||||||
Reference in New Issue
Block a user