FINERACT-2148: Accelerate maturity to charge-off date when a regular/backdated repayment or other monetary activity occurs before/after the charge-off date.
This commit is contained in:
committed by
Adam Saghy
parent
8ab9c9ea9a
commit
75701daa09
+5
@@ -99,6 +99,7 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
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.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator;
|
||||
@@ -979,6 +980,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
} else {
|
||||
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.getLoanTransactions().add(refundTransaction);
|
||||
if (interestRefundTransaction != null) {
|
||||
|
||||
+28
@@ -287,6 +287,13 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
|
||||
if (reprocessRequired) {
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = overpaidReprocess
|
||||
? loan.reprocessTransactionsWithPostTransactionChecks(transactionDate)
|
||||
: loan.reprocessTransactions();
|
||||
@@ -813,6 +820,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
if (reprocessRequired) {
|
||||
addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings()
|
||||
@@ -853,6 +866,12 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
loan.addLoanTransaction(loanChargeAdjustmentTransaction);
|
||||
if (loan.isInterestBearing() && loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) {
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
} else {
|
||||
loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction,
|
||||
@@ -1440,6 +1459,10 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), businessDate)) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
// Waive of charges whose due date falls after latest 'repayment' transaction don't require entire loan schedule
|
||||
// to be reprocessed.
|
||||
@@ -1451,6 +1474,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
* Consider removing this block of code or logically completing it for the future by getting the list of
|
||||
* affected Transactions
|
||||
*/
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
|
||||
+18
-1
@@ -1144,6 +1144,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
} else if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
return loan.reprocessTransactions();
|
||||
}
|
||||
@@ -3624,6 +3628,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
changedTransactionDetail = loan.reprocessTransactions();
|
||||
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
||||
loan.doPostLoanTransactionChecks(lastLoanTransactionDate, loanLifecycleStateMachine);
|
||||
@@ -3738,7 +3747,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && !(loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff))) {
|
||||
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
|
||||
} else {
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
@@ -3809,6 +3820,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
"reversed");
|
||||
writeOffTransaction.reverse();
|
||||
loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loan);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
return loan.reprocessTransactions();
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -33,6 +33,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -41,6 +42,7 @@ 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.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.starter.AdvancedPaymentScheduleTransactionProcessorCondition;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
@@ -56,6 +58,8 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
|
||||
private final EMICalculator emiCalculator;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
|
||||
private static void simulateRepaymentForDisbursements(LoanTransaction lt, final AtomicReference<BigDecimal> refundFinal,
|
||||
List<LoanTransaction> collect) {
|
||||
@@ -81,6 +85,13 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
||||
List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
|
||||
loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
|
||||
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessResult = processor
|
||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), relatedRefundTransactionDate, transactionsToReprocess,
|
||||
loan.getCurrency(), installmentsToReprocess, loan.getActiveCharges());
|
||||
|
||||
+27
-22
@@ -83,29 +83,34 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
||||
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = getRelatedRepaymentScheduleInstallment(loan, businessDate);
|
||||
if (loan.isInterestBearing() && loanRepaymentScheduleInstallment != null) {
|
||||
List<LoanTransaction> transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing().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();
|
||||
if (!changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getCurrentTransactionToOldId().isEmpty()
|
||||
|| !changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getNewTransactionMappings().isEmpty()) {
|
||||
List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
||||
.getNewTransactionMappings().keySet().stream().toList();
|
||||
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
|
||||
replayedTransactions);
|
||||
}
|
||||
if (model != null) {
|
||||
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(model, loanRepaymentScheduleInstallment.getDueDate(),
|
||||
businessDate);
|
||||
if (dueAmounts != null) {
|
||||
BigDecimal interestPaid = loanRepaymentScheduleInstallment.getInterestPaid();
|
||||
BigDecimal dueInterest = dueAmounts.getDueInterest().getAmount();
|
||||
if (interestPaid == null) {
|
||||
return dueInterest;
|
||||
if (loan.isChargedOff()) {
|
||||
return loanRepaymentScheduleInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
|
||||
} else {
|
||||
List<LoanTransaction> transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing().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();
|
||||
if (!changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getCurrentTransactionToOldId().isEmpty()
|
||||
|| !changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft().getNewTransactionMappings()
|
||||
.isEmpty()) {
|
||||
List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
||||
.getNewTransactionMappings().keySet().stream().toList();
|
||||
log.warn("Reprocessed transactions show differences: There are unsaved changes of the following transactions: {}",
|
||||
replayedTransactions);
|
||||
}
|
||||
if (model != null) {
|
||||
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(model, loanRepaymentScheduleInstallment.getDueDate(),
|
||||
businessDate);
|
||||
if (dueAmounts != null) {
|
||||
BigDecimal interestPaid = loanRepaymentScheduleInstallment.getInterestPaid();
|
||||
BigDecimal dueInterest = dueAmounts.getDueInterest().getAmount();
|
||||
if (interestPaid == null) {
|
||||
return dueInterest;
|
||||
}
|
||||
return dueInterest.subtract(interestPaid);
|
||||
}
|
||||
return dueInterest.subtract(interestPaid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAgingApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -51,8 +52,11 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
|
||||
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.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||
import org.apache.fineract.portfolio.note.domain.Note;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -72,6 +76,8 @@ public class LoanReAgingServiceImpl {
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final NoteRepository noteRepository;
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
|
||||
public CommandProcessingResult reAge(Long loanId, JsonCommand command) {
|
||||
Loan loan = loanAssembler.assembleFrom(loanId);
|
||||
@@ -123,6 +129,12 @@ public class LoanReAgingServiceImpl {
|
||||
}
|
||||
reverseReAgeTransaction(reAgeTransaction, command);
|
||||
loanTransactionRepository.saveAndFlush(reAgeTransaction);
|
||||
if (loan.getLoanProductRelatedDetail() != null
|
||||
&& loan.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.PROGRESSIVE)
|
||||
&& loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isChargeOff)) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
loan.reprocessTransactions();
|
||||
loan.updateLoanScheduleDependentDerivedFields();
|
||||
persistNote(loan, command, changes);
|
||||
|
||||
Reference in New Issue
Block a user