Skip to content

Commit

Permalink
FINERACT-2148: update instalments interest with zero after charge off…
Browse files Browse the repository at this point in the history
… with interest recalculation enabled
  • Loading branch information
oleksii-novikov-onix committed Dec 5, 2024
1 parent a2c01d2 commit c1621bb
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.List;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
Expand Down Expand Up @@ -74,6 +75,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestPaid;

@Setter
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestWaived;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.github.resilience4j.retry.annotation.Retry;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -184,6 +186,7 @@
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.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
Expand All @@ -198,6 +201,7 @@
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException;
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
Expand All @@ -209,6 +213,7 @@
import org.apache.fineract.portfolio.loanaccount.serialization.LoanOfficerValidator;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
Expand Down Expand Up @@ -279,12 +284,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final AccountTransferRepository accountTransferRepository;
private final LoanTransactionAssembler loanTransactionAssembler;
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
private final AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor;
private final LoanOfficerValidator loanOfficerValidator;
private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
private final LoanDisbursementService loanDisbursementService;
private final LoanScheduleService loanScheduleService;
private final LoanChargeValidator loanChargeValidator;
private final LoanOfficerService loanOfficerService;
private final ProgressiveEMICalculator emiCalculator;

@Transactional
@Override
Expand Down Expand Up @@ -3260,7 +3267,7 @@ public CommandProcessingResult chargeOff(JsonCommand command) {
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();

LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
loanTransactionRepository.saveAndFlush(chargeOffTransaction);
loan.addLoanTransaction(chargeOffTransaction);
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
Expand All @@ -3274,6 +3281,38 @@ public CommandProcessingResult chargeOff(JsonCommand command) {

postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargeOffPostBusinessEvent(chargeOffTransaction));

if (loan.getLoanProduct().isInterestRecalculationEnabled()) {
final Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> result = advancedPaymentScheduleTransactionProcessor
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), transactionDate,
loan.retrieveListOfTransactionsForReprocessing(), loan.getLoanRepaymentScheduleDetail().getCurrency(),
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());

final ProgressiveLoanInterestScheduleModel scheduleModel = result.getRight();

loan.getRepaymentScheduleInstallments().forEach(installment -> {
if (!installment.getFromDate().isAfter(transactionDate) && installment.getDueDate().isAfter(transactionDate)) {
final BigDecimal newInterest = emiCalculator
.getPeriodInterestTillDate(scheduleModel, installment.getDueDate(), transactionDate).getAmount();

final BigDecimal interestRemoved = installment.getInterestCharged().subtract(newInterest);
installment.updatePrincipal(defaultToZeroIfNull(installment.getPrincipal()).add(interestRemoved));
installment.setInterestWaived(defaultToZeroIfNull(installment.getInterestWaived()).add(interestRemoved));
installment.updateInterestCharged(newInterest);
}
});
} else {
calculatePartialPeriodInterest(loan, transactionDate);
}

loan.getRepaymentScheduleInstallments().forEach(installment -> {
if (installment.getFromDate().isAfter(transactionDate)) {
installment.updatePrincipal(defaultToZeroIfNull(installment.getPrincipal()).add(installment.getInterestCharged()));
installment.setInterestWaived(defaultToZeroIfNull(installment.getInterestWaived()).add(installment.getInterestCharged()));
installment.updateInterestCharged(BigDecimal.ZERO);
}
});

return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(chargeOffTransaction.getId()) //
Expand Down Expand Up @@ -3884,4 +3923,27 @@ public void closeAsMarkedForReschedule(final Loan loan, final JsonCommand comman

loanTransactionValidator.validateLoanRescheduleDate(loan);
}

private void calculatePartialPeriodInterest(final Loan loan, final LocalDate chargeOffDate) {
loan.getRepaymentScheduleInstallments().stream()
.filter(installment -> !installment.getFromDate().isAfter(chargeOffDate) && installment.getDueDate().isAfter(chargeOffDate))
.forEach(installment -> {
final BigDecimal totalInterest = installment.getInterestOutstanding(loan.getCurrency()).getAmount();
final long totalDaysInPeriod = ChronoUnit.DAYS.between(installment.getFromDate(), installment.getDueDate());
final long daysTillChargeOff = ChronoUnit.DAYS.between(installment.getFromDate(), chargeOffDate);

final BigDecimal interestTillChargeOff = totalInterest
.divide(BigDecimal.valueOf(totalDaysInPeriod), 10, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(daysTillChargeOff));

final BigDecimal interestRemoved = totalInterest.subtract(interestTillChargeOff);
installment.updatePrincipal(defaultToZeroIfNull(installment.getPrincipal()).add(interestRemoved));
installment.setInterestWaived(defaultToZeroIfNull(installment.getInterestWaived()).add(interestRemoved));
installment.updateInterestCharged(interestTillChargeOff);
});
}

private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
return possibleNullValue != null ? possibleNullValue : BigDecimal.ZERO;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
Expand Down Expand Up @@ -135,6 +136,7 @@
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
Expand Down Expand Up @@ -380,7 +382,8 @@ public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTr
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService,
LoanOfficerValidator loanOfficerValidator, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator,
LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService,
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService) {
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService,
AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor, ProgressiveEMICalculator progressiveEMICalculator) {
return new LoanWritePlatformServiceJpaRepositoryImpl(transactionProcessorFactory, context, loanTransactionValidator,
loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository,
loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService,
Expand All @@ -394,8 +397,10 @@ public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTr
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService);
loanTransactionAssembler, loanAccrualsProcessingService, advancedPaymentScheduleTransactionProcessor,
loanOfficerValidator, loanDownPaymentTransactionValidator,
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService,
progressiveEMICalculator);
}

@Bean
Expand Down

0 comments on commit c1621bb

Please sign in to comment.