FINERACT-2080: Extract complex loan actions out from Loan entity
This commit is contained in:
+21
-1
@@ -397,7 +397,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
transactionDate, txnExternalId, loanTransactionType);
|
||||
|
||||
if (loanTransactionType.isRepaymentAtDisbursement()) {
|
||||
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||
handlePayDisbursementTransaction(loan, chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||
} else {
|
||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate,
|
||||
@@ -438,6 +438,26 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return newPaymentTransaction;
|
||||
}
|
||||
|
||||
private void handlePayDisbursementTransaction(final Loan loan, final Long chargeId, final LoanTransaction chargesPayment,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
LoanCharge charge = null;
|
||||
for (final LoanCharge loanCharge : loan.getCharges()) {
|
||||
if (loanCharge.isActive() && chargeId.equals(loanCharge.getId())) {
|
||||
charge = loanCharge;
|
||||
}
|
||||
}
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, charge.amount(), null);
|
||||
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
|
||||
final Money zero = Money.zero(loan.getCurrency());
|
||||
chargesPayment.updateComponents(zero, zero, charge.getAmount(loan.getCurrency()), zero);
|
||||
chargesPayment.updateLoan(loan);
|
||||
loan.addLoanTransaction(chargesPayment);
|
||||
loan.updateLoanOutstandingBalances();
|
||||
charge.markAsFullyPaid();
|
||||
}
|
||||
|
||||
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
|
||||
postJournalEntries(loanAccount, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, false);
|
||||
|
||||
+3
-2
@@ -112,6 +112,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
private final ThreadPoolTaskExecutor taskExecutor;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
private final LoanChargeService loanChargeService;
|
||||
|
||||
/**
|
||||
* method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan
|
||||
@@ -733,7 +734,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
continue;
|
||||
}
|
||||
final BigDecimal chargeAmount = MathUtil.toBigDecimal(chargePortion);
|
||||
final LoanCharge loanCharge = loan.fetchLoanChargesById(accrualCharge.getLoanChargeId());
|
||||
final LoanCharge loanCharge = loanChargeService.fetchLoanChargesById(loan, accrualCharge.getLoanChargeId());
|
||||
final LoanChargePaidBy paidBy = new LoanChargePaidBy(transaction, loanCharge, chargeAmount, installment.getInstallmentNumber());
|
||||
loanCharge.getLoanChargePaidBySet().add(paidBy);
|
||||
transaction.getLoanChargesPaid().add(paidBy);
|
||||
@@ -845,7 +846,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
Money chargeAmount = loanCharge.getAmount(loan.getCurrency());
|
||||
if (chargeAmount.isNotEqualTo(accrualTransaction.getAmount(loan.getCurrency()))) {
|
||||
accrualTransaction.reverse();
|
||||
loan.handleChargeAppliedTransaction(loanCharge, accrualTransaction.getTransactionDate());
|
||||
loanChargeService.handleChargeAppliedTransaction(loan, loanCharge, accrualTransaction.getTransactionDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -832,7 +832,7 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
loan.updateLoanCharges(possiblyModifiedLoanCharges);
|
||||
loanChargeService.updateLoanCharges(loan, possiblyModifiedLoanCharges);
|
||||
}
|
||||
|
||||
// update installment level delinquency
|
||||
|
||||
+2
-2
@@ -296,8 +296,8 @@ public class LoanChargeAssembler {
|
||||
if (percentage == null) {
|
||||
percentage = chargeDefinition.getAmount();
|
||||
}
|
||||
loanCharge = loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation()),
|
||||
percentage);
|
||||
loanCharge = LoanCharge.calculatePerInstallmentChargeAmount(loan,
|
||||
ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation()), percentage);
|
||||
}
|
||||
|
||||
// If charge type is specified due date and loan is multi disburment
|
||||
|
||||
+3
-3
@@ -474,7 +474,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<>(3);
|
||||
if (loan.getActiveCharges().contains(loanCharge)) {
|
||||
final BigDecimal amount = loan.calculateAmountPercentageAppliedTo(loanCharge);
|
||||
final BigDecimal amount = loanChargeService.calculateAmountPercentageAppliedTo(loan, loanCharge);
|
||||
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount);
|
||||
changes.putAll(loanChargeChanges);
|
||||
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||
@@ -1115,7 +1115,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
// the loan product uses Upfront Accruals, or only when the loan are closed too,
|
||||
if ((loan.getStatus().isActive() && loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct())
|
||||
|| loan.getStatus().isOverpaid() || loan.getStatus().isClosedObligationsMet()) {
|
||||
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
|
||||
final LoanTransaction applyLoanChargeTransaction = loanChargeService.handleChargeAppliedTransaction(loan, loanCharge, null);
|
||||
if (applyLoanChargeTransaction != null) {
|
||||
this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
|
||||
businessEventNotifierService
|
||||
@@ -1123,7 +1123,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
} else if (configurationDomainService.isImmediateChargeAccrualPostMaturityEnabled()
|
||||
&& DateUtils.getBusinessLocalDate().isAfter(loan.getMaturityDate())) {
|
||||
final LoanTransaction loanTransaction = loan.createChargeAppliedTransaction(loanCharge, null);
|
||||
final LoanTransaction loanTransaction = loanChargeService.createChargeAppliedTransaction(loan, loanCharge, null);
|
||||
this.loanTransactionRepository.saveAndFlush(loanTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(loanTransaction));
|
||||
}
|
||||
|
||||
+2
-2
@@ -204,7 +204,7 @@ public class LoanDisbursementService {
|
||||
}
|
||||
} else if (disbursedOn.equals(loan.getActualDisbursementDate())
|
||||
&& loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
||||
loan.handleChargeAppliedTransaction(charge, disbursedOn);
|
||||
loanChargeService.handleChargeAppliedTransaction(loan, charge, disbursedOn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ public class LoanDisbursementService {
|
||||
} else {
|
||||
if (!loanChargeIds.isEmpty() && loanChargeIds.size() != chargeIdLength) {
|
||||
for (Long chargeId : loanChargeIds) {
|
||||
final LoanCharge deleteCharge = loan.fetchLoanChargesById(chargeId);
|
||||
final LoanCharge deleteCharge = loanChargeService.fetchLoanChargesById(loan, chargeId);
|
||||
if (loan.getCharges().contains(deleteCharge)) {
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, deleteCharge);
|
||||
loanChargeValidator.validateLoanChargeIsNotWaived(loan, deleteCharge);
|
||||
|
||||
+26
-1
@@ -530,7 +530,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CurrencyData currencyData = applicationCurrency.toData();
|
||||
|
||||
final LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction();
|
||||
final LoanTransaction waiveOfInterest = deriveDefaultInterestWaiverTransaction(loan);
|
||||
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST);
|
||||
|
||||
@@ -2235,4 +2235,29 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
public boolean existsByLoanId(Long loanId) {
|
||||
return loanRepositoryWrapper.existsByLoanId(loanId);
|
||||
}
|
||||
|
||||
private LoanTransaction deriveDefaultInterestWaiverTransaction(Loan loan) {
|
||||
final Money totalInterestOutstanding = loan.getTotalInterestOutstandingOnLoan();
|
||||
Money possibleInterestToWaive = totalInterestOutstanding.copy();
|
||||
LocalDate transactionDate = DateUtils.getBusinessLocalDate();
|
||||
|
||||
if (totalInterestOutstanding.isGreaterThanZero()) {
|
||||
// find earliest known instance of overdue interest and default to
|
||||
// that
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
for (final LoanRepaymentScheduleInstallment scheduledRepayment : installments) {
|
||||
|
||||
final Money outstandingForPeriod = scheduledRepayment.getInterestOutstanding(loan.getCurrency());
|
||||
if (scheduledRepayment.isOverdueOn(DateUtils.getBusinessLocalDate()) && scheduledRepayment.isNotFullyPaidOff()
|
||||
&& outstandingForPeriod.isGreaterThanZero()) {
|
||||
transactionDate = scheduledRepayment.getDueDate();
|
||||
possibleInterestToWaive = outstandingForPeriod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LoanTransaction.waiver(loan.getOffice(), loan, possibleInterestToWaive, transactionDate, possibleInterestToWaive,
|
||||
possibleInterestToWaive.zero(), ExternalId.empty());
|
||||
}
|
||||
}
|
||||
|
||||
+118
-2
@@ -107,6 +107,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.holiday.domain.Holiday;
|
||||
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
@@ -139,12 +140,14 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarType;
|
||||
import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
||||
import org.apache.fineract.portfolio.collectionsheet.command.SingleDisbursalCommand;
|
||||
import org.apache.fineract.portfolio.collectionsheet.command.SingleRepaymentCommand;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.group.domain.Group;
|
||||
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -2003,10 +2006,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
existingReversedTransactionIds);
|
||||
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
} else if (rescheduleBasedOnMeetingDates != null && rescheduleBasedOnMeetingDates) {
|
||||
loan.updateLoanRepaymentScheduleDates(calendar.getRecurrence(), isHolidayEnabled, holidays, workingDays,
|
||||
updateLoanRepaymentScheduleDates(loan, calendar.getRecurrence(), isHolidayEnabled, holidays, workingDays,
|
||||
presentMeetingDate, newMeetingDate, isSkipRepaymentOnFirstMonth, numberOfDays);
|
||||
} else {
|
||||
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
|
||||
updateLoanRepaymentScheduleDates(loan, calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
|
||||
holidays, workingDays, isSkipRepaymentOnFirstMonth, numberOfDays);
|
||||
}
|
||||
|
||||
@@ -2021,6 +2024,119 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLoanRepaymentScheduleDates(final Loan loan, final LocalDate meetingStartDate, final String recuringRule,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean isSkipRepaymentonfirstdayofmonth, final Integer numberofDays) {
|
||||
// first repayment's from date is same as disbursement date.
|
||||
LocalDate tmpFromDate = loan.getDisbursementDate();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType();
|
||||
final Integer loanRepaymentInterval = loan.getLoanRepaymentScheduleDetail().getRepayEvery();
|
||||
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
||||
|
||||
LocalDate newRepaymentDate;
|
||||
LocalDate latestRepaymentDate = null;
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) {
|
||||
LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
|
||||
|
||||
// FIXME: AA this won't update repayment dates before current date.
|
||||
if (DateUtils.isAfter(oldDueDate, meetingStartDate) && DateUtils.isDateInTheFuture(oldDueDate)) {
|
||||
newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate,
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
|
||||
final LocalDate maxDateLimitForNewRepayment = getMaxDateLimitForNewRepayment(repaymentPeriodFrequencyType,
|
||||
loanRepaymentInterval, tmpFromDate);
|
||||
|
||||
if (DateUtils.isAfter(newRepaymentDate, maxDateLimitForNewRepayment)) {
|
||||
newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, meetingStartDate, tmpFromDate,
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays);
|
||||
}
|
||||
|
||||
if (isHolidayEnabled) {
|
||||
newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays);
|
||||
}
|
||||
if (DateUtils.isBefore(latestRepaymentDate, newRepaymentDate)) {
|
||||
latestRepaymentDate = newRepaymentDate;
|
||||
}
|
||||
|
||||
loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate);
|
||||
// reset from date to get actual daysInPeriod
|
||||
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
|
||||
tmpFromDate = newRepaymentDate;// update with new repayment date
|
||||
} else {
|
||||
tmpFromDate = oldDueDate;
|
||||
}
|
||||
}
|
||||
if (latestRepaymentDate != null) {
|
||||
loan.setExpectedMaturityDate(latestRepaymentDate);
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDate getMaxDateLimitForNewRepayment(final PeriodFrequencyType periodFrequencyType, final Integer loanRepaymentInterval,
|
||||
final LocalDate startDate) {
|
||||
LocalDate dueRepaymentPeriodDate = startDate;
|
||||
final int repaidEvery = 2 * loanRepaymentInterval;
|
||||
switch (periodFrequencyType) {
|
||||
case DAYS -> dueRepaymentPeriodDate = startDate.plusDays(repaidEvery);
|
||||
case WEEKS -> dueRepaymentPeriodDate = startDate.plusWeeks(repaidEvery);
|
||||
case MONTHS -> dueRepaymentPeriodDate = startDate.plusMonths(repaidEvery);
|
||||
case YEARS -> dueRepaymentPeriodDate = startDate.plusYears(repaidEvery);
|
||||
case INVALID, WHOLE_TERM -> {
|
||||
}
|
||||
}
|
||||
return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from startDate
|
||||
}
|
||||
|
||||
private void updateLoanRepaymentScheduleDates(final Loan loan, final String recurringRule, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final LocalDate presentMeetingDate, final LocalDate newMeetingDate,
|
||||
final boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays) {
|
||||
// first repayment's from date is same as disbursement date.
|
||||
// meetingStartDate is used as seedDate Capture the seedDate from user and use the seedDate as meetingStart date
|
||||
|
||||
LocalDate tmpFromDate = loan.getDisbursementDate();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType();
|
||||
final Integer loanRepaymentInterval = loan.getLoanRepaymentScheduleDetail().getRepayEvery();
|
||||
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
||||
|
||||
LocalDate newRepaymentDate;
|
||||
boolean isFirstTime = true;
|
||||
LocalDate latestRepaymentDate = null;
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) {
|
||||
LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
|
||||
if (!DateUtils.isBefore(oldDueDate, presentMeetingDate)) {
|
||||
if (isFirstTime) {
|
||||
isFirstTime = false;
|
||||
newRepaymentDate = newMeetingDate;
|
||||
} else {
|
||||
// tmpFromDate.plusDays(1) is done to make sure
|
||||
// getNewRepaymentMeetingDate method returns next meeting
|
||||
// date and not the same as tmpFromDate
|
||||
newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recurringRule, tmpFromDate, tmpFromDate.plusDays(1),
|
||||
loanRepaymentInterval, frequency, workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays);
|
||||
}
|
||||
|
||||
if (isHolidayEnabled) {
|
||||
newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays);
|
||||
}
|
||||
if (DateUtils.isBefore(latestRepaymentDate, newRepaymentDate)) {
|
||||
latestRepaymentDate = newRepaymentDate;
|
||||
}
|
||||
loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate);
|
||||
// reset from date to get actual daysInPeriod
|
||||
|
||||
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
|
||||
|
||||
tmpFromDate = newRepaymentDate;// update with new repayment date
|
||||
} else {
|
||||
tmpFromDate = oldDueDate;
|
||||
}
|
||||
}
|
||||
if (latestRepaymentDate != null) {
|
||||
loan.setExpectedMaturityDate(latestRepaymentDate);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLoanCycle(final Loan loan) {
|
||||
final List<Loan> loansToUpdate;
|
||||
if (loan.isGroupLoan()) {
|
||||
|
||||
+38
-1
@@ -21,14 +21,18 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||
@@ -83,7 +87,7 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
loan.removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge);
|
||||
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loan, loanCharge);
|
||||
|
||||
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loan.getCurrency())) {
|
||||
/*
|
||||
@@ -100,6 +104,39 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final Loan loan, final LoanCharge loanCharge) {
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
LoanTransaction transactionToRemove = null;
|
||||
List<LoanTransaction> transactions = loan.getLoanTransactions();
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
if (transaction.isRepaymentAtDisbursement()
|
||||
&& doesLoanChargePaidByContainLoanCharge(transaction.getLoanChargesPaid(), loanCharge)) {
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
final Money chargeAmount = Money.of(currency, loanCharge.amount());
|
||||
if (transaction.isGreaterThan(chargeAmount)) {
|
||||
final Money principalPortion = Money.zero(currency);
|
||||
final Money interestPortion = Money.zero(currency);
|
||||
final Money penaltyChargesPortion = Money.zero(currency);
|
||||
|
||||
transaction.updateComponentsAndTotal(principalPortion, interestPortion, chargeAmount, penaltyChargesPortion);
|
||||
|
||||
} else {
|
||||
transactionToRemove = transaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transactionToRemove != null) {
|
||||
loan.removeLoanTransaction(transactionToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doesLoanChargePaidByContainLoanCharge(Set<LoanChargePaidBy> loanChargePaidBys, LoanCharge loanCharge) {
|
||||
return loanChargePaidBys.stream() //
|
||||
.anyMatch(loanChargePaidBy -> loanChargePaidBy.getLoanCharge().equals(loanCharge));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processLatestTransaction(final LoanTransaction loanTransaction, final Loan loan) {
|
||||
final ChangedTransactionDetail changedTransactionDetail = loanTransactionProcessingService.processLatestTransaction(
|
||||
|
||||
+3
-3
@@ -198,7 +198,7 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
when(loan.getLoanCharges()).thenReturn(new HashSet<>());
|
||||
when(loan.getDisbursementDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
|
||||
when(loan.getRepaymentScheduleInstallments()).thenReturn(new ArrayList<>());
|
||||
when(loan.calculateAmountPercentageAppliedTo(any(LoanCharge.class))).thenReturn(BigDecimal.TEN);
|
||||
when(loanChargeService.calculateAmountPercentageAppliedTo(any(Loan.class), any(LoanCharge.class))).thenReturn(BigDecimal.TEN);
|
||||
when(loan.fetchNumberOfInstallmensAfterExceptions()).thenReturn(5);
|
||||
when(loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(any(BigDecimal.class))).thenReturn(null);
|
||||
when(loan.deriveSumTotalOfChargesDueAtDisbursement()).thenReturn(BigDecimal.ZERO);
|
||||
@@ -219,8 +219,8 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
void shouldHandleAccrualBasedOnConfigurationAndDates(boolean isAccrualEnabled, LocalDate businessDate, LocalDate maturityDate, boolean isAccrualExpected) {
|
||||
when(configurationDomainService.isImmediateChargeAccrualPostMaturityEnabled()).thenReturn(isAccrualEnabled);
|
||||
when(loan.getMaturityDate()).thenReturn(maturityDate);
|
||||
when(loan.handleChargeAppliedTransaction(loanCharge, null)).thenReturn(loanTransaction);
|
||||
when(loan.createChargeAppliedTransaction(loanCharge, null)).thenReturn(loanTransaction);
|
||||
when(loanChargeService.handleChargeAppliedTransaction(loan, loanCharge, null)).thenReturn(loanTransaction);
|
||||
when(loanChargeService.createChargeAppliedTransaction(loan, loanCharge, null)).thenReturn(loanTransaction);
|
||||
|
||||
if (isAccrualExpected) {
|
||||
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(true);
|
||||
|
||||
Reference in New Issue
Block a user