FINERACT-2081: Payments after loan charge - payment allocation

This commit is contained in:
Marta Jankovics
2024-09-25 10:31:08 +02:00
committed by Adam Saghy
parent 2d0c8db993
commit 31c9079ad9
17 changed files with 746 additions and 228 deletions
@@ -209,29 +209,34 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
boolean isAppliedOnBackDate = false;
LoanCharge loanCharge = null;
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
LocalDate transactionDate = null;
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
ExternalId externalId = externalIdFactory.createFromCommand(command, "externalId");
boolean needToGenerateNewExternalId = false;
boolean isFirst = true;
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
if (disbursementDetail.actualDisbursementDate() == null) {
// If multiple charges to be applied, only the first one will get the provided externalId, for the
// rest we generate new ones (if needed)
if (needToGenerateNewExternalId) {
if (!isFirst) {
externalId = externalIdFactory.create();
}
LocalDate dueDate = disbursementDetail.expectedDisbursementDateAsLocalDate();
loanCharge = loanChargeAssembler.createNewWithoutLoan(chargeDefinition, disbursementDetail.principal(), null, null,
null, disbursementDetail.expectedDisbursementDateAsLocalDate(), null, null, externalId);
null, dueDate, null, null, externalId);
loanTrancheDisbursementCharge = new LoanTrancheDisbursementCharge(loanCharge, disbursementDetail);
loanCharge.updateLoanTrancheDisbursementCharge(loanTrancheDisbursementCharge);
businessEventNotifierService.notifyPreBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
addCharge(loan, chargeDefinition, loanCharge);
isAppliedOnBackDate = true;
if (DateUtils.isAfter(recalculateFrom, disbursementDetail.expectedDisbursementDateAsLocalDate())) {
recalculateFrom = disbursementDetail.expectedDisbursementDateAsLocalDate();
if (DateUtils.isAfter(recalculateFrom, dueDate)) {
recalculateFrom = dueDate;
}
needToGenerateNewExternalId = true;
if (isFirst) {
transactionDate = loanCharge.getEffectiveDueDate();
}
isFirst = false;
}
}
if (loanCharge == null) {
@@ -246,35 +251,38 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
validateAddLoanCharge(loan, chargeDefinition, loanCharge);
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
if (loanCharge.getDueLocalDate() == null || DateUtils.isAfter(recalculateFrom, loanCharge.getDueLocalDate())) {
if (DateUtils.isAfter(recalculateFrom, loanCharge.getDueLocalDate())) {
isAppliedOnBackDate = true;
recalculateFrom = loanCharge.getDueLocalDate();
}
transactionDate = loanCharge.getEffectiveDueDate();
}
boolean reprocessRequired = true;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
// overpaid transactions will be reprocessed and pay this charge
boolean overpaidReprocess = !loanCharge.isDueAtDisbursement() && !loanCharge.isPaid() && loan.getStatus().isOverpaid();
if (!overpaidReprocess && loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
loan = runScheduleRecalculation(loan, recalculateFrom);
reprocessRequired = false;
}
this.loanWritePlatformService.updateOriginalSchedule(loan);
}
// [For Adv payment allocation strategy] check if charge due date is earlier than last transaction
// date, if yes trigger reprocess else no reprocessing
if (AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equals(loan.transactionProcessingStrategy())) {
if (!overpaidReprocess && AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY
.equals(loan.transactionProcessingStrategy())) {
// [For Adv payment allocation strategy] check if charge due date is earlier than last transaction
// date, if yes trigger reprocess else no reprocessing
LoanTransaction lastPaymentTransaction = loan.getLastTransactionForReprocessing();
if (lastPaymentTransaction != null) {
if (loanCharge.getEffectiveDueDate() != null
&& DateUtils.isAfter(loanCharge.getEffectiveDueDate(), lastPaymentTransaction.getTransactionDate())) {
reprocessRequired = false;
}
if (lastPaymentTransaction != null
&& DateUtils.isAfter(loanCharge.getEffectiveDueDate(), lastPaymentTransaction.getTransactionDate())) {
reprocessRequired = false;
}
}
if (reprocessRequired) {
ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions();
ChangedTransactionDetail changedTransactionDetail = overpaidReprocess
? loan.reprocessTransactionsWithPostTransactionChecks(transactionDate)
: loan.reprocessTransactions();
if (changedTransactionDetail != null) {
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
@@ -284,7 +292,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);