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.Embeddable;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
|
||||
@@ -36,6 +37,7 @@ public class MonetaryCurrency {
|
||||
@Column(name = "currency_multiplesof")
|
||||
private Integer inMultiplesOf;
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
private transient CurrencyData currencyData;
|
||||
|
||||
protected MonetaryCurrency() {
|
||||
|
||||
+3
-3
@@ -21,6 +21,7 @@ package org.apache.fineract.organisation.monetary.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Iterator;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
|
||||
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.
|
||||
if (currency.getInMultiplesOf() != null && currency.getDecimalPlaces() == 0 && currency.getInMultiplesOf() > 0
|
||||
&& amountScaled.doubleValue() > 0) {
|
||||
final double existingVal = amountScaled.doubleValue();
|
||||
amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, currency.getInMultiplesOf()));
|
||||
&& MathUtil.isGreaterThanZero(amountScaled)) {
|
||||
amountScaled = roundToMultiplesOf(amountScaled, currency.getInMultiplesOf());
|
||||
}
|
||||
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 |
|
||||
| | | 01 February 2025 | | 500.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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 | | 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 | | 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 | | 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:
|
||||
| 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 |
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 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:
|
||||
| Expected Disbursement On | Disbursed On | Principal | Net Disbursal Amount |
|
||||
| 01 January 2025 | | 300.0 | |
|
||||
@@ -8860,4 +8860,4 @@ Feature: Loan
|
||||
| 21 October 2025 | Accrual Activity | 3.32 | 0.0 | 0.52 | 0.0 | 2.8 | 0.0 | false | true |
|
||||
| 06 November 2025 | Accrual | 1.21 | 0.0 | 1.21 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
And Customer makes "AUTOPAY" repayment on "06 November 2025" with 35.28 EUR transaction amount
|
||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||
|
||||
@@ -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 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 |
|
||||
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:
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 1 | MONTHS | 01 May 2024 | 6 |
|
||||
# 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:
|
||||
# | frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
# | 1 | MONTHS | 01 May 2024 | 6 |
|
||||
|
||||
@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
|
||||
@@ -5014,6 +5014,8 @@ Feature: LoanReAging
|
||||
| 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 | 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
|
||||
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 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 |
|
||||
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
|
||||
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 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 |
|
||||
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
|
||||
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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
# --- Reversal of re-age transaction ---
|
||||
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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
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
|
||||
@@ -5416,12 +5422,12 @@ Feature: LoanReAging
|
||||
| | | 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 | | 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 | | 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.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.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.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:
|
||||
| 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 |
|
||||
@@ -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 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 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
|
||||
@TestRailId:C4154 @AdvancedPaymentAllocation
|
||||
@@ -5508,7 +5514,7 @@ Feature: LoanReAging
|
||||
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
|
||||
| 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
|
||||
When Admin sets the business date to "01 January 2024"
|
||||
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 |
|
||||
| 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"
|
||||
|
||||
|
||||
|
||||
+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.rescheduleloan.domain.LoanTermVariationsRepository;
|
||||
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;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -56,8 +56,8 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
private final LoanTermVariationsRepository loanTermVariationsRepository;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult createInterestPause(final ExternalId loanExternalId, final String startDateString,
|
||||
@@ -114,7 +114,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
loanTermVariationsRepository.delete(variation);
|
||||
loan.getLoanTermVariations().remove(variation);
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsDeletedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
@@ -141,7 +141,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
|
||||
LoanTermVariations updatedVariation = loanTermVariationsRepository.save(variation);
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
@@ -161,7 +161,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
|
||||
final LoanTermVariations savedVariation = loanTermVariationsRepository.saveAndFlush(variation);
|
||||
loan.getLoanTermVariations().add(savedVariation);
|
||||
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsAddedBusinessEvent(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) {
|
||||
flushModeHandler.withFlushMode(FlushModeType.COMMIT, () -> {
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
if (loan.isNotDisbursed()) {
|
||||
if (loan.getSummary() != null) {
|
||||
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.domain.ExternalId;
|
||||
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.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
@@ -901,4 +902,51 @@ public class LoanChargeService {
|
||||
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 (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
+12
-2
@@ -42,6 +42,7 @@ public class LoanScheduleService {
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
private final LoanScheduleComponent loanSchedule;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final ILoanUtilService loanUtilService;
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||
} else {
|
||||
@@ -123,11 +124,20 @@ public class LoanScheduleService {
|
||||
}
|
||||
}
|
||||
}
|
||||
loanTransactionProcessingService.processPostDisbursementTransactions(loan);
|
||||
}
|
||||
|
||||
public void handleRegenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO);
|
||||
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.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
||||
@@ -48,8 +47,6 @@ public interface LoanTransactionProcessingService {
|
||||
|
||||
LoanRepaymentScheduleTransactionProcessor getTransactionProcessor(String transactionProcessingStrategyCode);
|
||||
|
||||
Optional<ChangedTransactionDetail> processPostDisbursementTransactions(Loan loan);
|
||||
|
||||
LoanScheduleDTO getRecalculatedSchedule(ScheduleGeneratorDTO generatorDTO, Loan loan);
|
||||
|
||||
OutstandingAmountsDTO fetchPrepaymentDetail(ScheduleGeneratorDTO scheduleGeneratorDTO, LocalDate onDate, Loan loan);
|
||||
|
||||
+2
-10
@@ -18,25 +18,17 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public interface ReprocessLoanTransactionsService {
|
||||
|
||||
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, LocalDate transactionDate, List<LoanTransaction> newTransactions);
|
||||
|
||||
void processPostDisbursementTransactions(Loan loan);
|
||||
|
||||
void removeLoanCharge(Loan loan, LoanCharge loanCharge);
|
||||
void reprocessTransactionsWithoutChecks(Loan loan, List<LoanTransaction> newTransactions);
|
||||
|
||||
void processLatestTransaction(LoanTransaction loanTransaction, Loan loan);
|
||||
|
||||
|
||||
+2
-3
@@ -51,11 +51,10 @@ public class ProgressivePossibleNextRepaymentCalculationServiceImpl extends Abst
|
||||
Optional<ProgressiveLoanModel> progressiveLoanModel = interestScheduleModelRepository.findOneByLoan(loan);
|
||||
Optional<ProgressiveLoanInterestScheduleModel> optionalScheduleModel = interestScheduleModelRepository
|
||||
.extractModel(progressiveLoanModel);
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.orElseGet(
|
||||
() -> advancedPaymentScheduleTransactionProcessor.calculateInterestScheduleModel(loan.getId(), nextPaymentDueDate));
|
||||
if (scheduleModel == null) {
|
||||
if (optionalScheduleModel.isEmpty()) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = optionalScheduleModel.get();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), repaymentScheduleInstallments, Set.of(),
|
||||
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.LoanRepaymentScheduleInstallment;
|
||||
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.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
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.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
||||
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.util.LoopContext;
|
||||
import org.apache.fineract.util.LoopGuard;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
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";
|
||||
|
||||
private final EMICalculator emiCalculator;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final InterestRefundService interestRefundService;
|
||||
private final LoanScheduleComponent loanSchedule;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final SingleLoanChargeRepaymentScheduleProcessingWrapper loanChargeRepaymentScheduleProcessing;
|
||||
|
||||
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
final InterestRefundService interestRefundService, final ExternalIdFactory externalIdFactory,
|
||||
final LoanScheduleComponent loanSchedule, final LoanTransactionRepository loanTransactionRepository,
|
||||
public AdvancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator, final InterestRefundService interestRefundService,
|
||||
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
|
||||
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
|
||||
final LoanChargeService loanChargeService) {
|
||||
super(externalIdFactory, loanChargeValidator, loanBalanceService);
|
||||
this.emiCalculator = emiCalculator;
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
this.interestRefundService = interestRefundService;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
this.loanSchedule = loanSchedule;
|
||||
this.loanChargeService = loanChargeService;
|
||||
this.loanChargeRepaymentScheduleProcessing = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
|
||||
@@ -195,13 +186,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
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
|
||||
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
|
||||
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
|
||||
@@ -302,18 +286,6 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
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
|
||||
private static LoanTransaction getProcessedTransaction(final ChangedTransactionDetail changedTransactionDetail,
|
||||
final LoanTransaction transaction) {
|
||||
@@ -2937,9 +2909,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
|
||||
private void updateInstallmentsByModelForReAging(final LoanTransaction loanTransaction, final ProgressiveTransactionCtx ctx) {
|
||||
ctx.getModel().repaymentPeriods().forEach(rp -> {
|
||||
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream().filter(
|
||||
ri -> ri.getFromDate().equals(rp.getFromDate()) && ri.getDueDate().equals(rp.getDueDate()) && !ri.isDownPayment())
|
||||
.findFirst().orElse(null);
|
||||
final LoanRepaymentScheduleInstallment installment = ctx.getInstallments().stream()
|
||||
.filter(ri -> ri.getFromDate().equals(rp.getFromDate()) && !ri.isDownPayment()).findFirst().orElse(null);
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
if (installment != null) {
|
||||
installment.setFromDate(rp.getFromDate());
|
||||
@@ -2953,6 +2924,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
installment.updateInterestCharged(rp.getDueInterest().getAmount());
|
||||
}
|
||||
installment.setReAged(true);
|
||||
installment.setAdditional(false);
|
||||
installment.updateObligationsMet(ctx.getCurrency(), transactionDate);
|
||||
} else {
|
||||
final LoanRepaymentScheduleInstallment lastInstallment = ctx.getInstallments().getLast();
|
||||
@@ -3247,7 +3219,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
final BigDecimal interestRate = loan.getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate();
|
||||
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)
|
||||
.loanTermFrequency(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments())
|
||||
.loanTermPeriodFrequencyType(loanTransaction.getLoanReAgeParameter().getFrequencyType())
|
||||
|
||||
+6
-6
@@ -202,8 +202,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||
transactionDate);
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), transactionDate));
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
|
||||
OutstandingDetails outstandingAmounts = emiCalculator.getOutstandingAmountsTillDate(model, transactionDate);
|
||||
// TODO: We should add all the past due outstanding amounts as well
|
||||
OutstandingAmountsDTO result = new OutstandingAmountsDTO(currency) //
|
||||
@@ -245,8 +244,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
return Money.zero(loan.getCurrency());
|
||||
}
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||
.orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), targetDate));
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
|
||||
return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false);
|
||||
}
|
||||
|
||||
@@ -438,7 +436,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amountOrPercentage());
|
||||
@@ -456,7 +455,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
} else {
|
||||
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);
|
||||
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;
|
||||
|
||||
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.loanproduct.data.LoanConfigurationDetails;
|
||||
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.RecalculationFrequencyType;
|
||||
|
||||
public final class LoanConfigurationDetailsMapper {
|
||||
|
||||
@@ -36,7 +56,22 @@ public final class LoanConfigurationDetailsMapper {
|
||||
loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(),
|
||||
loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
|
||||
loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
|
||||
loan.isInterestRecalculationEnabled(), loan.getLoanInterestRecalculationDetails().getRestFrequencyType(),
|
||||
loan.getLoanInterestRecalculationDetails().getPreCloseInterestCalculationStrategy());
|
||||
loan.isInterestRecalculationEnabled(), getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan));
|
||||
}
|
||||
|
||||
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.time.LocalDate;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -69,6 +70,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final CodeValueRepository codeValueRepository;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -87,13 +89,13 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
final Money capitalizedIncomeAmount = calculateCapitalizedIncomeAmount(loan, transactionAmount);
|
||||
final LoanTransaction capitalizedIncomeTransaction = LoanTransaction.capitalizedIncome(loan, capitalizedIncomeAmount, paymentDetail,
|
||||
transactionDate, txnExternalId);
|
||||
// Update loan with capitalized income
|
||||
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
||||
// Add Loan Transaction classification
|
||||
addClassificationCodeToTransaction(command, LoanTransactionApiConstants.CAPITALIZED_INCOME_CLASSIFICATION_CODE,
|
||||
capitalizedIncomeTransaction);
|
||||
// Recalculate loan transactions
|
||||
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeTransaction);
|
||||
recalculateLoanTransactions(loan, capitalizedIncomeTransaction);
|
||||
// Update loan with capitalized income
|
||||
loan.addLoanTransaction(capitalizedIncomeTransaction);
|
||||
// Save and flush (PK is set)
|
||||
loanTransactionRepository.saveAndFlush(capitalizedIncomeTransaction);
|
||||
// Create capitalized income balances
|
||||
@@ -144,8 +146,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
|
||||
capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
|
||||
capitalizedIncomeAdjustment.setClassification(capitalizedIncome.get().getClassification());
|
||||
recalculateLoanTransactions(loan, capitalizedIncomeAdjustment);
|
||||
loan.addLoanTransaction(capitalizedIncomeAdjustment);
|
||||
recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeAdjustment);
|
||||
LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
|
||||
|
||||
// Update outstanding loan balances
|
||||
@@ -182,9 +184,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
.build();
|
||||
}
|
||||
|
||||
private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
|
||||
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
private void recalculateLoanTransactions(Loan loan, LoanTransaction transaction) {
|
||||
if (loan.isInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transaction.getTransactionDate())) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(transaction));
|
||||
} else {
|
||||
reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
|
||||
}
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
|
||||
public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate) {
|
||||
Optional<ProgressiveLoanModel> progressiveLoanModel = findOneByLoanId(loan.getId());
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel;
|
||||
if (progressiveLoanModel.isPresent() && !progressiveLoanModel.get().getBusinessDate().isAfter(businessDate)) {
|
||||
if (progressiveLoanModel.isPresent()) {
|
||||
savedModel = extractModel(progressiveLoanModel);
|
||||
if (savedModel.isPresent() && progressiveLoanModel.get().getBusinessDate().isBefore(businessDate)) {
|
||||
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.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.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.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.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -58,9 +50,9 @@ import org.springframework.stereotype.Component;
|
||||
public class InternalProgressiveLoanApiResource implements InitializingBean {
|
||||
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
|
||||
private final InterestScheduleModelRepositoryWrapper writePlatformService;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
@Override
|
||||
@SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
|
||||
@@ -89,28 +81,15 @@ public class InternalProgressiveLoanApiResource implements InitializingBean {
|
||||
}
|
||||
|
||||
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) {
|
||||
final List<LoanTransaction> transactionsToReprocess = loanTransactionRepository
|
||||
.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||
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;
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||
return interestScheduleModelRepositoryWrapper.extractModel(interestScheduleModelRepositoryWrapper.findOneByLoan(loan)).get();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{loanId}/model")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Update and Save ProgressiveLoanInterestScheduleModel", description = "DO NOT USE THIS IN PRODUCTION!")
|
||||
@Transactional
|
||||
public ProgressiveLoanInterestScheduleModel updateModel(@PathParam("loanId") @Parameter(description = "loanId") long loanId) {
|
||||
Loan loan = loanRepository.findOneWithNotFoundDetection(loanId);
|
||||
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.LoanJournalEntryPoster;
|
||||
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.ProgressiveLoanTransactionValidatorImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||
@@ -59,11 +60,11 @@ public class ProgressiveLoanAccountConfiguration {
|
||||
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine, BusinessEventNotifierService businessEventNotifierService,
|
||||
CodeValueRepository codeValueRepository) {
|
||||
CodeValueRepository codeValueRepository, LoanScheduleService loanScheduleService) {
|
||||
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
||||
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
||||
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, loanLifecycleStateMachine,
|
||||
businessEventNotifierService, codeValueRepository);
|
||||
businessEventNotifierService, codeValueRepository, loanScheduleService);
|
||||
}
|
||||
|
||||
@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.LoanPaymentAllocationRule;
|
||||
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.LoanTransactionRelation;
|
||||
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.transactionprocessor.MoneyHolder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||
@@ -120,9 +118,8 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(LoanRepositoryWrapper.class),
|
||||
Mockito.mock(InterestRefundService.class), Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class),
|
||||
Mockito.mock(LoanTransactionRepository.class), Mockito.mock(LoanChargeValidator.class),
|
||||
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, Mockito.mock(InterestRefundService.class),
|
||||
Mockito.mock(ExternalIdFactory.class), Mockito.mock(LoanScheduleComponent.class), Mockito.mock(LoanChargeValidator.class),
|
||||
Mockito.mock(LoanBalanceService.class), Mockito.mock(LoanChargeService.class));
|
||||
|
||||
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.portfolio.loanaccount.domain.Loan;
|
||||
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.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -67,7 +67,7 @@ public class InternalCOBApiResource implements InitializingBean {
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final ToApiJsonSerializer<List> toApiJsonSerializerForList;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
protected DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
|
||||
|
||||
@@ -113,7 +113,7 @@ public class InternalCOBApiResource implements InitializingBean {
|
||||
@Path("loan-reprocess/{loanId}")
|
||||
@Transactional
|
||||
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 {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
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.LoanTransactionRepository;
|
||||
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.exception.InstallmentNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||
@@ -277,20 +274,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
|
||||
if (reprocessRequired) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
if (overpaidReprocess) {
|
||||
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan, transactionDate);
|
||||
} else {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
}
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate
|
||||
@@ -600,7 +589,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
||||
loanChargeService.removeLoanCharge(loan, loanCharge);
|
||||
this.loanRepositoryWrapper.save(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanDeleteChargeBusinessEvent(loanCharge));
|
||||
return new CommandProcessingResultBuilder() //
|
||||
@@ -857,9 +846,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
if (reprocessRequired) {
|
||||
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -885,22 +872,16 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT);
|
||||
loanChargeAdjustmentTransaction.getLoanTransactionRelations().add(loanTransactionRelation);
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(loanChargeAdjustmentTransaction));
|
||||
} else {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
reprocessLoanTransactionsService.processLatestTransaction(loanChargeAdjustmentTransaction, loan);
|
||||
}
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||
|
||||
@@ -1444,12 +1425,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge,
|
||||
waiveLoanChargeTransaction.getAmount(loan.getCurrency()).getAmount(), loanInstallmentNumber);
|
||||
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
|
||||
loan.addLoanTransaction(waiveLoanChargeTransaction);
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
// 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 LoanDisbursementValidator loanDisbursementValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final LoanJournalEntryPoster loanJournalEntryPoster;
|
||||
@@ -326,7 +325,7 @@ public class LoanDisbursementService {
|
||||
for (LoanCharge loanCharge : tempCharges) {
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, loanCharge);
|
||||
reprocessLoanTransactionsService.removeLoanCharge(loan, loanCharge);
|
||||
loanChargeService.removeLoanCharge(loan, loanCharge);
|
||||
}
|
||||
loan.getTrancheCharges().clear();
|
||||
} else {
|
||||
@@ -336,7 +335,7 @@ public class LoanDisbursementService {
|
||||
if (loan.getCharges().contains(deleteCharge)) {
|
||||
loanChargeValidator.validateLoanIsNotClosed(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); //
|
||||
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
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
||||
// the loan's state
|
||||
if (txCount != afterRemovalTxCount || chargeCount != afterRemovalChargeCount) {
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
LoanArrearsData arrearsData = arrearsAgingService.calculateArrearsForLoan(loan);
|
||||
|
||||
+4
-31
@@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -65,7 +64,6 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
||||
private final LoanTermVariationsMapper loanMapper;
|
||||
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final LoanTransactionService loanTransactionService;
|
||||
|
||||
@Override
|
||||
@@ -131,32 +129,6 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
||||
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
|
||||
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO, Loan loan) {
|
||||
if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff()) {
|
||||
@@ -232,9 +204,10 @@ public class LoanTransactionProcessingServiceImpl implements LoanTransactionProc
|
||||
AdvancedPaymentScheduleTransactionProcessor advancedProcessor, Loan loan, LoanTransaction loanTransaction) {
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan,
|
||||
loanTransaction.getTransactionDate());
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
||||
|
||||
if (savedModel.isEmpty()) {
|
||||
throw new IllegalArgumentException("No saved model found for loan transaction " + loanTransaction);
|
||||
}
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.get();
|
||||
ProgressiveTransactionCtx progressiveContext = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||
new ChangedTransactionDetail(), model, getTotalRefundInterestAmount(loan));
|
||||
|
||||
+9
-25
@@ -21,11 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.organisation.holiday.domain.Holiday;
|
||||
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.group.domain.Group;
|
||||
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.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LoanUtilService {
|
||||
public class LoanUtilService implements ILoanUtilService {
|
||||
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
@@ -74,17 +70,20 @@ public class LoanUtilService {
|
||||
private final CalendarReadPlatformService calendarReadPlatformService;
|
||||
private final NoteRepository noteRepository;
|
||||
|
||||
@Override
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) {
|
||||
final HolidayDetailDTO holidayDetailDTO = null;
|
||||
return buildScheduleGeneratorDTO(loan, recalculateFrom, null, holidayDetailDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom,
|
||||
final LocalDate rescheduleTill) {
|
||||
final HolidayDetailDTO holidayDetailDTO = null;
|
||||
return buildScheduleGeneratorDTO(loan, recalculateFrom, rescheduleTill, holidayDetailDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom, final LocalDate recalculateTill,
|
||||
final HolidayDetailDTO holidayDetailDTO) {
|
||||
HolidayDetailDTO holidayDetails = holidayDetailDTO;
|
||||
@@ -147,6 +146,7 @@ public class LoanUtilService {
|
||||
return scheduleGeneratorDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isLoanRepaymentsSyncWithMeeting(final Group group, final Calendar calendar) {
|
||||
Boolean isSkipRepaymentOnFirstMonth = false;
|
||||
Long entityId = null;
|
||||
@@ -170,6 +170,7 @@ public class LoanUtilService {
|
||||
return isSkipRepaymentOnFirstMonth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) {
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
@@ -190,6 +191,7 @@ public class LoanUtilService {
|
||||
return holidayDetailDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HolidayDetailDTO constructHolidayDTO(final Long officeId, LocalDate localDate) {
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, localDate,
|
||||
@@ -225,16 +227,6 @@ public class LoanUtilService {
|
||||
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,
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper) {
|
||||
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
|
||||
@@ -305,6 +297,7 @@ public class LoanUtilService {
|
||||
return calculatedRepaymentsStartingFromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) {
|
||||
if (!repaymentTransactionType.isRepaymentType()) {
|
||||
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) {
|
||||
final Client client = loan.client();
|
||||
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,
|
||||
newInterestPaymentWaiverTransaction);
|
||||
|
||||
if (newInterestPaymentWaiverTransaction.isNotZero()) {
|
||||
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
|
||||
|
||||
boolean reprocess = true;
|
||||
@@ -970,6 +966,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(),
|
||||
newInterestPaymentWaiverTransaction, new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||
loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
reprocess = false;
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
|
||||
@@ -982,22 +979,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!reprocess) {
|
||||
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||
}
|
||||
}
|
||||
if (reprocess) {
|
||||
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
||||
reprocessChangedLoanTransactions(loan, newInterestPaymentWaiverTransaction, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
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()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
loan.addLoanTransaction(newInterestPaymentWaiverTransaction);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -1304,13 +1304,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
validateLoanTransactionAmountChargeBack(loanTransaction, newTransaction);
|
||||
|
||||
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) {
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled() && loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
// Store the Loan Transaction Relation
|
||||
@@ -2285,11 +2281,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.getLoanProductRelatedDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
reprocessLoanTransactionsService.processPostDisbursementTransactions(loan);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2441,9 +2435,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|| rescheduledRepaymentDate != null) {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
@@ -2757,9 +2749,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
final List<LoanTransaction> loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||
loanTransactions.add(chargeOffTransaction);
|
||||
reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(chargeOffTransaction));
|
||||
loan.addLoanTransaction(chargeOffTransaction);
|
||||
} else {
|
||||
reprocessLoanTransactionsService.processLatestTransaction(chargeOffTransaction, loan);
|
||||
@@ -3010,9 +3000,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
} else {
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.addLoanTransaction(interestRefundTxn);
|
||||
@@ -3059,13 +3047,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction) {
|
||||
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
||||
|
||||
loan.addLoanTransaction(chargebackTransaction);
|
||||
if (loan.isInterestBearing() && loan.isInterestRecalculationEnabled()) {
|
||||
final List<LoanTransaction> transactions = loanTransactionService.retrieveListOfTransactionsForReprocessing(loan);
|
||||
loanTransactionProcessingService.reprocessLoanTransactions(loan.getTransactionProcessingStrategyCode(),
|
||||
loan.getDisbursementDate(), transactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||
loan.getActiveCharges());
|
||||
loan.addLoanTransaction(chargebackTransaction);
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
} else {
|
||||
loan.addLoanTransaction(chargebackTransaction);
|
||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), chargebackTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
@@ -3252,16 +3238,24 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LoanTransaction loanTransaction = LoanTransaction.writeoff(loan, loan.getOffice(), writtenOffOnLocalDate, externalId);
|
||||
LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
|
||||
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,
|
||||
writtenOffOnLocalDate);
|
||||
}
|
||||
|
||||
loan.addLoanTransaction(loanTransaction);
|
||||
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& DateUtils.isBeforeBusinessDate(loanTransaction.getTransactionDate())) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
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);
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, loan);
|
||||
changes.put(PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
|
||||
@@ -3272,17 +3266,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan.isDisbursementAllowed() && loan.atLeastOnceDisbursed()) {
|
||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getDisbursedAmount());
|
||||
loan.removeDisbursementDetail();
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction()
|
||||
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
if (loan.isCumulativeSchedule()) {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
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()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
} else if (loan.isProgressiveSchedule()) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
@@ -3422,7 +3411,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
&& DateUtils.isEqual(loanTermVariations.getTermApplicableFrom(), lastTransactionDate))
|
||||
|| DateUtils.isAfter(loanTermVariations.getTermApplicableFrom(), lastTransactionDate));
|
||||
reverseExistingTransactionsTillLastDisbursal(loan, lastDisbursalTransaction);
|
||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan, scheduleGeneratorDTO);
|
||||
actualChanges.put("undolastdisbursal", "true");
|
||||
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||
@@ -3442,8 +3431,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
"reversed");
|
||||
writeOffTransaction.reverse();
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+3
-5
@@ -81,11 +81,9 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
LocalDate relatedRefundTransactionDate, List<LoanTransaction> transactionsToReprocess) {
|
||||
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
||||
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);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
|
||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessResult = processor
|
||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), relatedRefundTransactionDate, transactionsToReprocess,
|
||||
|
||||
+1
-28
@@ -21,23 +21,16 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
|
||||
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.LoanRepaymentScheduleInstallment;
|
||||
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.LoanSchedulePeriodData;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||
@@ -51,10 +44,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Slf4j
|
||||
public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
|
||||
|
||||
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
|
||||
private final EMICalculator emiCalculator;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||
|
||||
@Override
|
||||
@@ -81,24 +72,6 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
||||
&& 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
|
||||
public BigDecimal computeTotalUnpaidPayableNotDueInterestAmountOnActualPeriod(final Loan loan,
|
||||
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);
|
||||
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElseGet(() -> calculateModel(loan, businessDate));
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElse(null);
|
||||
if (model != null) {
|
||||
OutstandingDetails outstandingDetails = emiCalculator.getOutstandingAmountsTillDate(model, businessDate);
|
||||
if (!loan.isInterestRecalculationEnabled()) {
|
||||
|
||||
+16
-97
@@ -19,10 +19,8 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.annotation.WithFlushMode;
|
||||
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.LoanTransactionBusinessEvent;
|
||||
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.loanaccount.data.TransactionChangeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
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.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -78,111 +71,41 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reprocessParticularTransactions(final Loan loan, final List<LoanTransaction> loanTransactions) {
|
||||
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, loanTransactions);
|
||||
handleChangedDetail(changedTransactionDetail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
|
||||
public void reprocessTransactions(final Loan loan, final List<LoanTransaction> newTransactions) {
|
||||
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||
transactions.addAll(newTransactions);
|
||||
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, transactions);
|
||||
handleChangedDetail(changedTransactionDetail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reprocessTransactionsWithoutChecks(final Loan loan, final LocalDate transactionDate,
|
||||
final List<LoanTransaction> newTransactions) {
|
||||
public void reprocessTransactionsWithoutChecks(final Loan loan, final List<LoanTransaction> newTransactions) {
|
||||
final List<LoanTransaction> transactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||
transactions.addAll(newTransactions);
|
||||
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
|
||||
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
||||
LoanRepaymentScheduleTransactionProcessor transactionProcessor = loanTransactionProcessingService
|
||||
.getTransactionProcessor(loan.getTransactionProcessingStrategyCode());
|
||||
|
||||
TransactionCtx transactionCtx;
|
||||
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedProcessor) {
|
||||
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor) {
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||
loanTransaction.getTransactionDate());
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel
|
||||
.orElseGet(() -> advancedProcessor.calculateInterestScheduleModel(loan.getId(), loanTransaction.getTransactionDate()));
|
||||
if (savedModel.isEmpty()) {
|
||||
throw new IllegalArgumentException("No saved model found for loan transaction " + loanTransaction);
|
||||
} else {
|
||||
|
||||
final ProgressiveTransactionCtx progressiveTransactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||
new ChangedTransactionDetail(), model);
|
||||
progressiveTransactionCtx.setChargedOff(loan.isChargedOff());
|
||||
progressiveTransactionCtx.setWrittenOff(loan.isClosedWrittenOff());
|
||||
progressiveTransactionCtx.setContractTerminated(loan.isContractTermination());
|
||||
transactionCtx = progressiveTransactionCtx;
|
||||
final ProgressiveTransactionCtx progressiveTransactionCtx = new ProgressiveTransactionCtx(loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), new MoneyHolder(loan.getTotalOverpaidAsMoney()),
|
||||
new ChangedTransactionDetail(), savedModel.get());
|
||||
progressiveTransactionCtx.setChargedOff(loan.isChargedOff());
|
||||
progressiveTransactionCtx.setWrittenOff(loan.isClosedWrittenOff());
|
||||
progressiveTransactionCtx.setContractTerminated(loan.isContractTermination());
|
||||
transactionCtx = progressiveTransactionCtx;
|
||||
}
|
||||
} else {
|
||||
transactionCtx = new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail());
|
||||
@@ -201,11 +124,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
|
||||
@Override
|
||||
public void updateModel(Loan loan) {
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan,
|
||||
ThreadLocalContextUtil.getBusinessDate());
|
||||
if (savedModel.isEmpty()) {
|
||||
reprocessTransactions(loan);
|
||||
}
|
||||
interestScheduleModelRepositoryWrapper.getSavedModel(loan, ThreadLocalContextUtil.getBusinessDate());
|
||||
}
|
||||
|
||||
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.LoanDownPaymentHandlerService;
|
||||
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.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
@@ -100,6 +101,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||
private final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
@Override
|
||||
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)) {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
loanScheduleService.regenerateScheduleWithReprocessingTransactions(loan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-18
@@ -76,7 +76,7 @@ public class LoanContractTerminationServiceImpl {
|
||||
private final LoanTransactionService loanTransactionService;
|
||||
|
||||
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
|
||||
loanUtilService.checkClientOrGroupActive(loan);
|
||||
|
||||
@@ -87,7 +87,19 @@ public class LoanContractTerminationServiceImpl {
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
|
||||
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");
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
@@ -95,22 +107,7 @@ public class LoanContractTerminationServiceImpl {
|
||||
final Note note = Note.loanTransactionNote(loan, contractTermination, noteText);
|
||||
noteRepository.save(note);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
loanTransactionRepository.saveAndFlush(contractTermination);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
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.reaging.LoanReAgeInterestHandlingType;
|
||||
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.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||
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.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanRepaymentScheduleService;
|
||||
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.ReprocessLoanTransactionsService;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
@@ -100,14 +99,16 @@ public class LoanReAgingService {
|
||||
private final LoanRepaymentScheduleService loanRepaymentScheduleService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
|
||||
private final InterestScheduleModelRepositoryWrapper modelRepository;
|
||||
private final LoanTransactionService loanTransactionService;
|
||||
|
||||
public CommandProcessingResult reAge(final Long loanId, final JsonCommand command) {
|
||||
final Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
reAgingValidator.validateReAge(loan, command);
|
||||
|
||||
final LoanTransaction reAgeTransaction = createReAgeTransaction(loan, command);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
processReAgeTransaction(loan, reAgeTransaction, true);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
@@ -170,8 +171,7 @@ public class LoanReAgingService {
|
||||
if (reAgeTransaction == null) {
|
||||
throw new LoanTransactionNotFoundException("Re-Age transaction for loan was not found");
|
||||
}
|
||||
if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
|
||||
|| loan.hasContractTerminationTransaction() || (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
|
||||
if (loan.isProgressiveSchedule()) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
@@ -196,31 +196,16 @@ public class LoanReAgingService {
|
||||
}
|
||||
|
||||
private void processReAgeTransaction(final Loan loan, final LoanTransaction reAgeTransaction, final boolean withPostTransactionChecks) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
if (reAgeTransaction.getTransactionDate().isBefore(reAgeTransaction.getSubmittedOnDate())
|
||||
&& !loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
if (reAgeTransaction.getTransactionDate().isBefore(reAgeTransaction.getSubmittedOnDate())) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
if (withPostTransactionChecks) {
|
||||
reprocessLoanTransactionsService.reprocessTransactionsWithPostTransactionChecks(loan,
|
||||
reAgeTransaction.getTransactionDate());
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(reAgeTransaction));
|
||||
} else {
|
||||
reprocessLoanTransactionsService.reprocessTransactionsWithoutChecks(loan, reAgeTransaction.getTransactionDate(),
|
||||
List.of(reAgeTransaction));
|
||||
reprocessLoanTransactionsService.reprocessTransactionsWithoutChecks(loan, 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 {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(reAgeTransaction,
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
reprocessLoanTransactionsService.processLatestTransaction(reAgeTransaction, loan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-7
@@ -21,8 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
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.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -136,12 +134,11 @@ public class LoanAccountAutoStarter {
|
||||
@Bean
|
||||
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
|
||||
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(final EMICalculator emiCalculator,
|
||||
final LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
final @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService,
|
||||
final ExternalIdFactory externalIdFactory, final LoanScheduleComponent loanSchedule,
|
||||
final LoanTransactionRepository loanTransactionRepository, final LoanChargeValidator loanChargeValidator,
|
||||
final LoanBalanceService loanBalanceService, @Lazy final LoanChargeService loanChargeService) {
|
||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService,
|
||||
externalIdFactory, loanSchedule, loanTransactionRepository, loanChargeValidator, loanBalanceService, loanChargeService);
|
||||
final LoanChargeValidator loanChargeValidator, final LoanBalanceService loanBalanceService,
|
||||
@Lazy final LoanChargeService loanChargeService) {
|
||||
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, progressiveLoanInterestRefundService, externalIdFactory,
|
||||
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.GLIMAccountInfoWritePlatformService;
|
||||
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.LoanAccountServiceImpl;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
|
||||
@@ -495,11 +496,10 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
||||
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanChargeService loanChargeService, LoanBalanceService loanBalanceService, LoanJournalEntryPoster journalEntryPoster,
|
||||
LoanTransactionRepository loanTransactionRepository) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService,
|
||||
loanChargeService, loanBalanceService, journalEntryPoster, loanTransactionRepository);
|
||||
LoanDisbursementValidator loanDisbursementValidator, LoanChargeService loanChargeService, LoanBalanceService loanBalanceService,
|
||||
LoanJournalEntryPoster journalEntryPoster, LoanTransactionRepository loanTransactionRepository) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, loanChargeService, loanBalanceService,
|
||||
journalEntryPoster, loanTransactionRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -515,9 +515,9 @@ public class LoanAccountConfiguration {
|
||||
public LoanScheduleService loanScheduleService(final LoanChargeService loanChargeService,
|
||||
final ReprocessLoanTransactionsService reprocessLoanTransactionsService, final LoanMapper loanMapper,
|
||||
final LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule,
|
||||
final LoanTransactionRepository loanTransactionRepository) {
|
||||
final LoanTransactionRepository loanTransactionRepository, final ILoanUtilService loanUtilService) {
|
||||
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanMapper, loanTransactionProcessingService,
|
||||
loanSchedule, loanTransactionRepository);
|
||||
loanSchedule, loanTransactionRepository, loanUtilService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -544,9 +544,9 @@ public class LoanAccountConfiguration {
|
||||
@ConditionalOnMissingBean(InterestPauseWritePlatformService.class)
|
||||
public InterestPauseWritePlatformService interestPauseWritePlatformService(LoanTermVariationsRepository loanTermVariationsRepository,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper, LoanAssembler loanAssembler,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, BusinessEventNotifierService businessEventNotifierService) {
|
||||
BusinessEventNotifierService businessEventNotifierService, LoanScheduleService loanScheduleService) {
|
||||
return new InterestPauseWritePlatformServiceImpl(loanTermVariationsRepository, loanRepositoryWrapper, loanAssembler,
|
||||
reprocessLoanTransactionsService, businessEventNotifierService);
|
||||
businessEventNotifierService, loanScheduleService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+3
@@ -165,6 +165,9 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
@Mock
|
||||
private LoanJournalEntryPoster journalEntryPoster;
|
||||
|
||||
@Mock
|
||||
private LoanScheduleService loanScheduleService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||
|
||||
Reference in New Issue
Block a user