FINERACT-2338: Allow backdated interest change on progressive loans
- charge-off handling - write-off handling - closed loans - external owner changes
This commit is contained in:
+1
@@ -62,6 +62,7 @@ public class ProgressivePossibleNextRepaymentCalculationServiceImpl extends Abst
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), repaymentScheduleInstallments, Set.of(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), scheduleModel);
|
||||
ctx.setChargedOff(loan.isChargedOff());
|
||||
ctx.setWrittenOff(loan.isClosedWrittenOff());
|
||||
ctx.setContractTerminated(loan.isContractTermination());
|
||||
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(nextPaymentDueDate, ctx, false);
|
||||
RepaymentPeriod repaymentPeriod = scheduleModel.findRepaymentPeriodByDueDate(nextPaymentDueDate)
|
||||
|
||||
+31
-26
@@ -1495,6 +1495,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
|
||||
protected void handleWriteOff(final LoanTransaction transaction, TransactionCtx ctx) {
|
||||
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx) {
|
||||
updateInstallmentsPrincipalAndInterestByModel(progressiveTransactionCtx);
|
||||
progressiveTransactionCtx.setChargedOff(true);
|
||||
}
|
||||
super.handleWriteOff(transaction, ctx.getCurrency(), ctx.getInstallments());
|
||||
}
|
||||
|
||||
@@ -1548,8 +1552,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
public void recalculateInterestForDate(LocalDate targetDate, ProgressiveTransactionCtx ctx, boolean updateInstallments) {
|
||||
if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()) {
|
||||
Loan loan = ctx.getInstallments().getFirst().getLoan();
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isNpa() && !ctx.isChargedOff()
|
||||
&& !ctx.isContractTerminated() && !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) {
|
||||
if (isInterestRecalculationSupported(ctx, loan) && !loan.isNpa()
|
||||
&& !loan.getLoanInterestRecalculationDetails().disallowInterestCalculationOnPastDue()) {
|
||||
|
||||
boolean modelHasUpdates = rework(targetDate, ctx);
|
||||
if (modelHasUpdates && updateInstallments) {
|
||||
@@ -2313,9 +2317,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
boolean interestBearingAndInterestRecalculationEnabled = loanTransaction.getLoan()
|
||||
.isInterestBearingAndInterestRecalculationEnabled();
|
||||
|
||||
if (interestBearingAndInterestRecalculationEnabled && ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& !progressiveTransactionCtx.isChargedOff() && !progressiveTransactionCtx.isContractTerminated()) {
|
||||
if (isInterestRecalculationSupported(ctx, loanTransaction.getLoan())) {
|
||||
// Clear any previously skipped installments before re-evaluating
|
||||
ProgressiveTransactionCtx progressiveTransactionCtx = (ProgressiveTransactionCtx) ctx;
|
||||
progressiveTransactionCtx.getSkipRepaymentScheduleInstallments().clear();
|
||||
paymentAllocationContext
|
||||
.setInAdvanceInstallmentsFilteringRules(installment -> loanTransaction.isBefore(installment.getDueDate())
|
||||
@@ -2389,15 +2393,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
context.getTransactionMappings(), context.getLoanTransaction(), oldestPastDueInstallment,
|
||||
context.getCtx().getCurrency());
|
||||
Loan loan = context.getLoanTransaction().getLoan();
|
||||
if (context.getCtx() instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& !progressiveTransactionCtx.isChargedOff()
|
||||
&& !progressiveTransactionCtx.isContractTerminated()) {
|
||||
context.setAllocatedAmount(
|
||||
handlingPaymentAllocationForInterestBearingProgressiveLoan(context.getLoanTransaction(),
|
||||
context.getTransactionAmountUnprocessed(), context.getBalances(),
|
||||
paymentAllocationType, oldestPastDueInstallment, progressiveTransactionCtx,
|
||||
loanTransactionToRepaymentScheduleMapping, oldestPastDueInstallmentCharges));
|
||||
if (isInterestRecalculationSupported(context.getCtx(), loan)) {
|
||||
context.setAllocatedAmount(handlingPaymentAllocationForInterestBearingProgressiveLoan(
|
||||
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
|
||||
context.getBalances(), paymentAllocationType, oldestPastDueInstallment,
|
||||
(ProgressiveTransactionCtx) context.getCtx(), loanTransactionToRepaymentScheduleMapping,
|
||||
oldestPastDueInstallmentCharges));
|
||||
} else {
|
||||
context.setAllocatedAmount(processPaymentAllocation(paymentAllocationType, oldestPastDueInstallment,
|
||||
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
|
||||
@@ -2418,14 +2419,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
context.getTransactionMappings(), context.getLoanTransaction(), dueInstallment,
|
||||
context.getCtx().getCurrency());
|
||||
Loan loan = context.getLoanTransaction().getLoan();
|
||||
if (context.getCtx() instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& !progressiveTransactionCtx.isChargedOff()
|
||||
&& !progressiveTransactionCtx.isContractTerminated()) {
|
||||
context.setAllocatedAmount(handlingPaymentAllocationForInterestBearingProgressiveLoan(
|
||||
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
|
||||
context.getBalances(), paymentAllocationType, dueInstallment, progressiveTransactionCtx,
|
||||
loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges));
|
||||
if (isInterestRecalculationSupported(context.getCtx(), loan)) {
|
||||
context.setAllocatedAmount(
|
||||
handlingPaymentAllocationForInterestBearingProgressiveLoan(context.getLoanTransaction(),
|
||||
context.getTransactionAmountUnprocessed(), context.getBalances(),
|
||||
paymentAllocationType, dueInstallment, (ProgressiveTransactionCtx) context.getCtx(),
|
||||
loanTransactionToRepaymentScheduleMapping, dueInstallmentCharges));
|
||||
} else {
|
||||
context.setAllocatedAmount(processPaymentAllocation(paymentAllocationType, dueInstallment,
|
||||
context.getLoanTransaction(), context.getTransactionAmountUnprocessed(),
|
||||
@@ -2472,13 +2471,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (inAdvanceInstallment.equals(inAdvanceInstallments.get(numberOfInstallments - 1))) {
|
||||
evenPortion = evenPortion.add(balanceAdjustment);
|
||||
}
|
||||
if (context.getCtx() instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& !progressiveTransactionCtx.isChargedOff()
|
||||
&& !progressiveTransactionCtx.isContractTerminated()) {
|
||||
if (isInterestRecalculationSupported(context.getCtx(), loan)) {
|
||||
context.setAllocatedAmount(handlingPaymentAllocationForInterestBearingProgressiveLoan(
|
||||
context.getLoanTransaction(), evenPortion, context.getBalances(), paymentAllocationType,
|
||||
inAdvanceInstallment, progressiveTransactionCtx,
|
||||
inAdvanceInstallment, (ProgressiveTransactionCtx) context.getCtx(),
|
||||
loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges));
|
||||
} else {
|
||||
context.setAllocatedAmount(processPaymentAllocation(paymentAllocationType, inAdvanceInstallment,
|
||||
@@ -3070,4 +3066,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.fetchFirstNormalInstallmentNumber(getCtx().getInstallments());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInterestRecalculationSupported(TransactionCtx ctx, Loan loan) {
|
||||
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx) {
|
||||
return loan.isInterestBearingAndInterestRecalculationEnabled() && !progressiveTransactionCtx.isChargedOff()
|
||||
&& !progressiveTransactionCtx.isWrittenOff() && !progressiveTransactionCtx.isContractTerminated();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -37,16 +37,18 @@ import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanIntere
|
||||
public class ProgressiveTransactionCtx extends TransactionCtx {
|
||||
|
||||
private final ProgressiveLoanInterestScheduleModel model;
|
||||
private List<LoanTransaction> alreadyProcessedTransactions = new ArrayList<>();
|
||||
private final List<LoanTransaction> alreadyProcessedTransactions = new ArrayList<>();
|
||||
@Setter
|
||||
private Money sumOfInterestRefundAmount;
|
||||
@Setter
|
||||
private boolean isChargedOff = false;
|
||||
@Setter
|
||||
private boolean isWrittenOff = false;
|
||||
@Setter
|
||||
private boolean isContractTerminated = false;
|
||||
@Setter
|
||||
private boolean isPrepayAttempt = false;
|
||||
private List<LoanRepaymentScheduleInstallment> skipRepaymentScheduleInstallments = new ArrayList<>();
|
||||
private final List<LoanRepaymentScheduleInstallment> skipRepaymentScheduleInstallments = new ArrayList<>();
|
||||
|
||||
public ProgressiveTransactionCtx(MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments,
|
||||
Set<LoanCharge> charges, MoneyHolder overpaymentHolder, ChangedTransactionDetail changedTransactionDetail,
|
||||
|
||||
+22
-2
@@ -25,6 +25,7 @@ import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.Loan
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateApprovalDate;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateForOverdueCharges;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateLoanIsActive;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateLoanStatusIsActiveOrClosed;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonComment;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleReasonId;
|
||||
import static org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidatorImpl.validateRescheduleRequestStatus;
|
||||
@@ -47,7 +48,9 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
@@ -70,7 +73,6 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
|
||||
final JsonElement jsonElement = jsonCommand.parsedJson();
|
||||
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
validateSubmittedOnDate(fromJsonHelper, loan, jsonElement, dataValidatorBuilder);
|
||||
final LocalDate rescheduleFromDate = validateAndRetrieveRescheduleFromDate(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||
validateRescheduleReasonId(fromJsonHelper, jsonElement, dataValidatorBuilder);
|
||||
@@ -81,6 +83,11 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
|
||||
boolean hasInterestRateChange = interestRate != null;
|
||||
boolean hasAdjustDueDateChange = adjustedDueDate != null;
|
||||
if (hasInterestRateChange) {
|
||||
validateLoanStatusIsActiveOrClosed(loan, dataValidatorBuilder);
|
||||
} else {
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
}
|
||||
|
||||
if (hasInterestRateChange && hasAdjustDueDateChange) {
|
||||
dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.adjustedDueDateParamName).failWithCode(
|
||||
@@ -144,7 +151,20 @@ public class ProgressiveLoanRescheduleRequestDataValidator implements LoanResche
|
||||
LocalDate rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
LoanRepaymentScheduleInstallment installment;
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
|
||||
boolean hasInterestRateChange = false;
|
||||
for (LoanRescheduleRequestToTermVariationMapping mapping : loanRescheduleRequest
|
||||
.getLoanRescheduleRequestToTermVariationMappings()) {
|
||||
LoanTermVariationType termType = mapping.getLoanTermVariations().getTermType();
|
||||
if (termType.isInterestRateVariation() || termType.isInterestRateFromInstallment()) {
|
||||
hasInterestRateChange = true;
|
||||
}
|
||||
}
|
||||
if (hasInterestRateChange) {
|
||||
validateLoanStatusIsActiveOrClosed(loan, dataValidatorBuilder);
|
||||
} else {
|
||||
validateLoanIsActive(loan, dataValidatorBuilder);
|
||||
}
|
||||
|
||||
if (loanRescheduleRequest.getInterestRateFromInstallmentTermVariationIfExists() != null) {
|
||||
installment = loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate);
|
||||
|
||||
+1
@@ -113,6 +113,7 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
|
||||
Set.of(), new MoneyHolder(loan.getTotalOverpaidAsMoney()), new ChangedTransactionDetail(), savedModel.get());
|
||||
ctx.setChargedOff(loan.isChargedOff());
|
||||
ctx.setWrittenOff(loan.isClosedWrittenOff());
|
||||
ctx.setContractTerminated(loan.isContractTermination());
|
||||
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(businessDate, ctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user