FINERACT-1773: Allow 'specific due date charges' to be applied after the maturity date
This commit is contained in:
committed by
Arnold Gálovics
parent
12e548ad82
commit
b33e3ac892
+15
-14
@@ -37,17 +37,20 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
|
||||
@Override
|
||||
public LoanStatus dryTransition(final LoanEvent loanEvent, final Loan loan) {
|
||||
return getNextState(loanEvent, loan);
|
||||
LoanStatus nextStatus = getNextState(loanEvent, loan);
|
||||
return nextStatus != null ? nextStatus : loan.status();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(final LoanEvent loanEvent, final Loan loan) {
|
||||
LoanStatus newState = getNextState(loanEvent, loan);
|
||||
loan.setLoanStatus(newState.getValue());
|
||||
if (newState != null) {
|
||||
loan.setLoanStatus(newState.getValue());
|
||||
|
||||
if (isNotLoanCreation(loanEvent)) {
|
||||
// in case of Loan creation, a LoanCreatedBusinessEvent is also raised, no need to send a status change
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanStatusChangedBusinessEvent(loan));
|
||||
if (isNotLoanCreation(loanEvent)) {
|
||||
// in case of Loan creation, a LoanCreatedBusinessEvent is also raised, no need to send a status change
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanStatusChangedBusinessEvent(loan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
}
|
||||
|
||||
LoanStatus from = loan.status();
|
||||
LoanStatus newState = from;
|
||||
LoanStatus newState = null;
|
||||
|
||||
switch (loanEvent) {
|
||||
case LOAN_REJECTED:
|
||||
@@ -97,7 +100,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
break;
|
||||
case LOAN_CHARGE_PAYMENT:
|
||||
case LOAN_REPAYMENT_OR_WAIVER:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
@@ -116,11 +119,6 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
newState = closedRescheduleOutstandingAmountTransition();
|
||||
}
|
||||
break;
|
||||
case INTERST_REBATE_OWED:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
||||
newState = closeObligationsMetTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_OVERPAYMENT:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.ACTIVE)) {
|
||||
newState = overpaidTransition();
|
||||
@@ -141,8 +139,6 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
case LOAN_WITHDRAW_TRANSFER:
|
||||
newState = activeTransition();
|
||||
break;
|
||||
case LOAN_CLOSED:
|
||||
break;
|
||||
case WRITE_OFF_OUTSTANDING_UNDO:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_WRITTEN_OFF)) {
|
||||
newState = activeTransition();
|
||||
@@ -151,6 +147,11 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
case LOAN_CREDIT_BALANCE_REFUND:
|
||||
newState = closeObligationsMetTransition();
|
||||
break;
|
||||
case LOAN_CHARGE_ADDED:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_CHARGEBACK:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
newState = activeTransition();
|
||||
|
||||
+8
-4
@@ -572,8 +572,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
public void addLoanCharge(final LoanCharge loanCharge) {
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
|
||||
if (isChargesAdditionAllowed() && loanCharge.isDueAtDisbursement()) {
|
||||
// Note: added this constraint to restrict adding disbursement
|
||||
// charges to a loan
|
||||
@@ -596,7 +594,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
loanCharge.update(this);
|
||||
|
||||
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||
BigDecimal chargeAmt;
|
||||
BigDecimal totalChargeAmt = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
@@ -626,6 +624,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), charges());
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, this);
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail reprocessTransactions() {
|
||||
@@ -743,7 +742,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
|
||||
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate,
|
||||
final LocalDate lastRepaymentPeriodDueDate) {
|
||||
if (loanCharge.isSpecifiedDueDate()
|
||||
if (isInterestBearing() && loanCharge.isSpecifiedDueDate()
|
||||
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
|
||||
final String defaultUserMessage = "This charge with specified due date cannot be added as the it is not in schedule range.";
|
||||
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
|
||||
@@ -5242,6 +5241,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isInterestBearing() {
|
||||
return getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& BigDecimal.ZERO.compareTo(getLoanRepaymentScheduleDetail().getAnnualNominalInterestRate()) < 0;
|
||||
}
|
||||
|
||||
public LocalDate getExpectedMaturityDate() {
|
||||
return this.expectedMaturityDate;
|
||||
}
|
||||
|
||||
+2
-1
@@ -49,5 +49,6 @@ public enum LoanEvent {
|
||||
LOAN_REJECT_TRANSFER, //
|
||||
LOAN_WITHDRAW_TRANSFER, //
|
||||
LOAN_CREDIT_BALANCE_REFUND, //
|
||||
LOAN_CHARGEBACK;
|
||||
LOAN_CHARGEBACK, //
|
||||
LOAN_CHARGE_ADDED;
|
||||
}
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
}
|
||||
|
||||
for (final LoanTransaction loanTransaction : transactionstoBeProcessed) {
|
||||
|
||||
// TODO: analyze and remove this
|
||||
if (!loanTransaction.getTypeOf().equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN)) {
|
||||
final Comparator<LoanRepaymentScheduleInstallment> byDate = new Comparator<LoanRepaymentScheduleInstallment>() {
|
||||
|
||||
|
||||
+16
@@ -1773,6 +1773,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
if (!loan.isInterestBearing() && loanCharge.isSpecifiedDueDate() && loanCharge.getDueDate().isAfter(loan.getMaturityDate())) {
|
||||
LoanRepaymentScheduleInstallment latestRepaymentScheduleInstalment = loan.getRepaymentScheduleInstallments()
|
||||
.get(loan.getLoanRepaymentScheduleInstallmentsSize() - 1);
|
||||
if (loanCharge.getDueDate().isAfter(latestRepaymentScheduleInstalment.getDueDate())) {
|
||||
if (latestRepaymentScheduleInstalment.isAdditional()) {
|
||||
latestRepaymentScheduleInstalment.updateDueDate(loanCharge.getDueDate());
|
||||
} else {
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan,
|
||||
(loan.getLoanRepaymentScheduleInstallmentsSize() + 1), latestRepaymentScheduleInstalment.getDueDate(),
|
||||
loanCharge.getDueDate(), BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null);
|
||||
installment.markAsAdditional();
|
||||
loan.addLoanRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loan.addLoanCharge(loanCharge);
|
||||
|
||||
this.loanChargeRepository.saveAndFlush(loanCharge);
|
||||
|
||||
Reference in New Issue
Block a user