private void addAccruals()

in fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java [331:445]


    private void addAccruals(@NotNull final Loan loan, @NotNull LocalDate tillDate, final boolean periodic, final boolean isFinal,
            final boolean addJournal, final boolean chargeOnDueDate) {
        if ((!isFinal && !loan.isOpen()) || loan.isNpa() || loan.isChargedOff()
                || !loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
            return;
        }

        final LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails();
        if (recalculationDetails != null && recalculationDetails.isCompoundingToBePostedAsTransaction()) {
            return;
        }
        final List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
        final LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
        reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
        ensureAccrualTransactionMappings(loan, existingAccruals, chargeOnDueDate);
        if (DateUtils.isAfter(tillDate, lastDueDate)) {
            tillDate = lastDueDate;
        }

        final boolean progressiveAccrual = isProgressiveAccrual(loan);
        final LocalDate accruedTill = loan.getAccruedTill();
        final LocalDate businessDate = DateUtils.getBusinessLocalDate();
        final LocalDate accrualDate = isFinal
                ? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate)
                        : getFinalAccrualTransactionDate(loan))
                : tillDate;
        if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)) {
            if (isFinal) {
                reverseTransactionsAfter(existingAccruals, accrualDate, addJournal);
            } else if (existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), accrualDate))) {
                return;
            }
        }

        final AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic, isFinal, chargeOnDueDate);
        final boolean mergeTransactions = isFinal || progressiveAccrual;
        final MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency();
        List<LoanTransaction> accrualTransactions = new ArrayList<>();
        Money totalInterestPortion = null;
        LoanTransaction mergeAccrualTransaction = null;
        LoanTransaction mergeAdjustTransaction = null;
        for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
            final Money interestAccruable = MathUtil.nullToZero(period.getInterestAccruable(), currency);
            final Money interestPortion = MathUtil.minus(interestAccruable, period.getInterestAccrued());
            final Money feeAccruable = MathUtil.nullToZero(period.getFeeAccruable(), currency);
            final Money feePortion = MathUtil.minus(feeAccruable, period.getFeeAccrued());
            final Money penaltyAccruable = MathUtil.nullToZero(period.getPenaltyAccruable(), currency);
            final Money penaltyPortion = MathUtil.minus(penaltyAccruable, period.getPenaltyAccrued());
            if (MathUtil.isEmpty(interestPortion) && MathUtil.isEmpty(feePortion) && MathUtil.isEmpty(penaltyPortion)) {
                continue;
            }
            if (mergeTransactions) {
                totalInterestPortion = MathUtil.plus(totalInterestPortion, interestPortion);
                if (progressiveAccrual) {
                    final Money feeAdjustmentPortion = MathUtil.negate(feePortion);
                    final Money penaltyAdjustmentPortion = MathUtil.negate(penaltyPortion);
                    mergeAdjustTransaction = createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, period,
                            accrualTransactions, null, feeAdjustmentPortion, penaltyAdjustmentPortion, true);
                }
                mergeAccrualTransaction = createOrMergeAccrualTransaction(loan, mergeAccrualTransaction, accrualDate, period,
                        accrualTransactions, null, feePortion, penaltyPortion, false);
            } else {
                final LocalDate dueDate = period.getDueDate();
                if (!isFinal && DateUtils.isAfter(dueDate, tillDate) && DateUtils.isBefore(tillDate, accruedTill)) {
                    continue;
                }
                final LocalDate periodAccrualDate = DateUtils.isBefore(dueDate, accrualDate) ? dueDate : accrualDate;
                final LoanTransaction accrualTransaction = addAccrualTransaction(loan, periodAccrualDate, period, interestPortion,
                        feePortion, penaltyPortion, false);
                if (accrualTransaction != null) {
                    accrualTransactions.add(accrualTransaction);
                }
            }
            final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
            installment.updateAccrualPortion(interestAccruable, feeAccruable, penaltyAccruable);
        }
        if (mergeTransactions && !MathUtil.isEmpty(totalInterestPortion)) {
            if (progressiveAccrual) {
                final Money interestAdjustmentPortion = MathUtil.negate(totalInterestPortion);
                createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, null, accrualTransactions,
                        interestAdjustmentPortion, null, null, true);
            }
            createOrMergeAccrualTransaction(loan, mergeAccrualTransaction, accrualDate, null, accrualTransactions, totalInterestPortion,
                    null, null, false);
        }
        if (accrualTransactions.isEmpty()) {
            return;
        }

        if (!isFinal || progressiveAccrual) {
            loan.setAccruedTill(isFinal ? accrualDate : tillDate);
        }

        accrualTransactions = loanTransactionRepository.saveAll(accrualTransactions);
        loanTransactionRepository.flush();

        if (addJournal) {
            final List<AccountingBridgeLoanTransactionDTO> newTransactionDTOs = new ArrayList<>();
            for (LoanTransaction accrualTransaction : accrualTransactions) {
                final LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
                        ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
                        : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
                businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
                final AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper
                        .mapToLoanTransactionData(accrualTransaction, currency.getCode());
                newTransactionDTOs.add(transactionDTO);
            }
            final AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(),
                    loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(),
                    loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(),
                    loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false,
                    false, false, null, newTransactionDTOs);
            this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
        }
    }