Skip to content

Commit

Permalink
FINERACT-2081: Apply penalty to overdue loans enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
Jose Alberto Hernandez committed Sep 5, 2024
1 parent 803a575 commit de5158e
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 75 deletions.
30 changes: 15 additions & 15 deletions fineract-e2e-tests-runner/src/test/resources/features/COB.feature
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ Feature: COBFeature
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_3 | 10 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
When Admin sets the business date to "12 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | |
| RANGE_1 | 04 August 2023 | 10 August 2023 |


Scenario: Verify that COB doesn’t touch CLOSED loans
Expand All @@ -89,22 +89,22 @@ Feature: COBFeature
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | |
| RANGE_1 | 04 August 2023 | 10 August 2023 |
And Customer makes "AUTOPAY" repayment on "10 August 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 10 August 2023 |
When Admin sets the business date to "11 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 10 August 2023 |


Scenario: Verify that COB doesn’t touch OVERPAID loans
Expand All @@ -121,22 +121,22 @@ Feature: COBFeature
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | |
| RANGE_1 | 04 August 2023 | 10 August 2023 |
And Customer makes "AUTOPAY" repayment on "10 August 2023" with 1200 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 10 August 2023 |
When Admin sets the business date to "11 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
| RANGE_3 | 10 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 10 August 2023 |

@Skip
Scenario: Verify that COB catch up runs properly on loan which is behind date because of locked with error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,7 @@ Feature: LoanDelinquency
| PAUSE | 15 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 250.0 | 04 October 2023 | 11 | 14 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 11 | 14 |
Then Installment level delinquency event has correct data
# --- Delinquency resume ---
When Admin sets the business date to "25 October 2023"
Expand All @@ -647,10 +644,7 @@ Feature: LoanDelinquency
| RESUME | 25 October 2023 | |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 500.0 | 04 October 2023 | 12 | 25 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
| NO_DELINQUENCY | 500.0 | 04 October 2023 | 12 | 25 |
Then Installment level delinquency event has correct data


Expand All @@ -674,10 +668,7 @@ Feature: LoanDelinquency
| PAUSE | 15 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 250.0 | 04 October 2023 | 11 | 14 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 11 | 14 |
Then Installment level delinquency event has correct data
# --- Delinquency resume ---
When Admin sets the business date to "25 October 2023"
Expand All @@ -696,10 +687,7 @@ Feature: LoanDelinquency
| RESUME | 25 October 2023 | |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 500.0 | 04 October 2023 | 12 | 25 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
| NO_DELINQUENCY | 500.0 | 04 October 2023 | 12 | 25 |
Then Installment level delinquency event has correct data
# --- Delinquency runs ---
When Admin sets the business date to "13 November 2023"
Expand Down Expand Up @@ -802,11 +790,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 5 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 5 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
Then Installment level delinquency event has correct data
# --- Full repayment for late/due date installments ---
When Admin sets the business date to "16 October 2023"
Expand All @@ -818,11 +803,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 15 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 15 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
And Customer makes "AUTOPAY" repayment on "16 October 2023" with 500 EUR transaction amount
When Admin runs inline COB job for Loan
Then Loan Delinquency pause periods has the following data:
Expand Down Expand Up @@ -857,11 +839,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 5 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 5 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
Then Installment level delinquency event has correct data
# --- Full repayment for late/due date installments ---
When Admin sets the business date to "16 October 2023"
Expand All @@ -873,11 +852,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 15 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 15 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
And Customer makes "AUTOPAY" repayment on "16 October 2023" with 150 EUR transaction amount
When Admin runs inline COB job for Loan
Then Loan Delinquency pause periods has the following data:
Expand Down Expand Up @@ -915,11 +891,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 5 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 5 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
Then Installment level delinquency event has correct data
# --- Full repayment for late/due date installments ---
When Admin sets the business date to "16 October 2023"
Expand All @@ -931,11 +904,8 @@ Feature: LoanDelinquency
| PAUSE | 06 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 04 October 2023 | 2 | 15 |
| NO_DELINQUENCY | 250.0 | 04 October 2023 | 2 | 15 |
# --- Grace period applied only on Loan level, not on installment level ---
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 250.00 |
And Customer makes "AUTOPAY" repayment on "16 October 2023" with 250 EUR transaction amount
When Admin runs inline COB job for Loan
Then Loan Delinquency pause periods has the following data:
Expand Down Expand Up @@ -1034,21 +1004,15 @@ Feature: LoanDelinquency
| true | 30 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 500.0 | 04 October 2023 | 21 | 29 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 500.00 |
| NO_DELINQUENCY | 500.0 | 04 October 2023 | 21 | 29 |
When Admin sets the business date to "31 October 2023"
Then Loan Delinquency pause periods has the following data:
| active | pausePeriodStart | pausePeriodEnd |
| false | 25 October 2023 | 30 October 2023 |
| false | 30 October 2023 | 30 October 2023 |
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_3 | 500.0 | 04 October 2023 | 22 | 30 |
Then Loan has the following INSTALLMENT level delinquency data:
| rangeId | Range | Amount |
| 2 | RANGE_3 | 500.00 |
| NO_DELINQUENCY | 500.0 | 04 October 2023 | 22 | 30 |


Scenario: Verify that creating a loan with Advanced payment allocation with product no Advanced payment allocation set results an error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ public class ApplyChargeToOverdueLoansBusinessStep implements LoanCOBBusinessSte
public Loan execute(Loan loan) {
final Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList = loanReadPlatformService
.retrieveAllOverdueInstallmentsForLoan(loan);
loanChargeWritePlatformService.applyOverdueChargesForLoan(loan.getId(), overdueLoanScheduleDataList);
if (!overdueLoanScheduleDataList.isEmpty()) {
loanChargeWritePlatformService.applyOverdueChargesForLoan(loan.getId(), overdueLoanScheduleDataList);
}
return loan;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon
List<Throwable> exceptions = new ArrayList<>();
for (Map.Entry<Long, Collection<OverdueLoanScheduleData>> entry : overdueScheduleData.entrySet()) {
try {
loanChargeWritePlatformService.applyOverdueChargesForLoan(entry.getKey(), entry.getValue());

if (!entry.getValue().isEmpty()) {
loanChargeWritePlatformService.applyOverdueChargesForLoan(entry.getKey(), entry.getValue());
}
} catch (final PlatformApiDataValidationException e) {
final List<ApiParameterError> errors = e.getErrors();
for (final ApiParameterError error : errors) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -757,11 +758,19 @@ public CommandProcessingResult adjustmentForLoanCharge(Long loanId, Long loanCha
@Transactional
@Override
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDataList) {
if (overdueLoanScheduleDataList.isEmpty()) {
return;
}
Loan loan = this.loanAssembler.assembleFrom(loanId);
if (loan.isChargedOff()) {
log.warn("Adding charge to Loan: {} is not allowed. Loan Account is Charged-off", loanId);
return;
}
Optional<Charge> optPenaltyCharge = loan.getLoanProduct().getCharges().stream()
.filter((e) -> ChargeTimeType.OVERDUE_INSTALLMENT.getValue().equals(e.getChargeTimeType()) && e.isLoanCharge()).findFirst();
if (optPenaltyCharge.isEmpty()) {
return;
}
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();
boolean runInterestRecalculation = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,15 @@ public Collection<OverdueLoanScheduleData> retrieveAllOverdueInstallmentsForLoan
if (!loan.isOpen()) {
return list;
}

Optional<Charge> optPenaltyCharge = loan.getLoanProduct().getCharges().stream()
.filter((e) -> ChargeTimeType.OVERDUE_INSTALLMENT.getValue().equals(e.getChargeTimeType()) && e.isLoanCharge()).findFirst();

if (optPenaltyCharge.isEmpty()) {
return list;
}
final Charge penaltyCharge = optPenaltyCharge.get();

final Long penaltyWaitPeriod = configurationDomainService.retrievePenaltyWaitPeriod();
final boolean backdatePenalties = configurationDomainService.isBackdatePenaltiesEnabled();

Expand All @@ -1657,16 +1666,9 @@ public Collection<OverdueLoanScheduleData> retrieveAllOverdueInstallmentsForLoan
if (!backdatePenalties && !isDueToday) {
continue;
}
Optional<Charge> penaltyCharge = loan.getLoanProduct().getCharges().stream()
.filter((e) -> ChargeTimeType.OVERDUE_INSTALLMENT.getValue().equals(e.getChargeTimeType()) && e.isLoanCharge())
.findFirst();

if (penaltyCharge.isEmpty()) {
continue;
}

list.add(new OverdueLoanScheduleData(loan.getId(), penaltyCharge.get().getId(),
DateUtils.DEFAULT_DATE_FORMATTER.format(installment.getDueDate()), penaltyCharge.get().getAmount(),
list.add(new OverdueLoanScheduleData(loan.getId(), penaltyCharge.getId(),
DateUtils.DEFAULT_DATE_FORMATTER.format(installment.getDueDate()), penaltyCharge.getAmount(),
DateUtils.DEFAULT_DATE_FORMAT, Locale.ENGLISH.toLanguageTag(),
installment.getPrincipalOutstanding(loan.getCurrency()).getAmount(),
installment.getInterestOutstanding(loan.getCurrency()).getAmount(), installment.getInstallmentNumber()));
Expand Down

0 comments on commit de5158e

Please sign in to comment.