[ https://issues.apache.org/jira/browse/FINERACT-167?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15322230#comment-15322230 ]
ASF GitHub Bot commented on FINERACT-167: ----------------------------------------- Github user nazeer1100126 commented on a diff in the pull request: https://github.com/apache/incubator-fineract/pull/130#discussion_r66409263 --- Diff: fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java --- @@ -5972,4 +5986,204 @@ public BigDecimal getDerivedAmountForCharge(LoanCharge loanCharge) { return amount; } + public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) { + Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding()); + Money[] receivables = retriveIncomeOutstandingTillDate(closureDate); + final List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null; + return new LoanRepaymentScheduleInstallment(null, 0, LocalDate.now(), LocalDate.now(), totalPrincipal.getAmount(), + receivables[0].getAmount(), receivables[1].getAmount(), receivables[2].getAmount(), false, compoundingDetails); + } + + public Money[] retriveIncomeOutstandingTillDate(final LocalDate paymentDate) { + Money[] balances = new Money[3]; + final MonetaryCurrency currency = getCurrency(); + Money interest = Money.zero(currency); + Money fee = Money.zero(currency); + Money penalty = Money.zero(currency); + boolean isArrearsPresent = false; + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + if (installment.isNotFullyPaidOff()) { + if (!isArrearsPresent || !installment.getDueDate().isAfter(paymentDate)) { + interest = interest.plus(installment.getInterestOutstanding(currency)); + fee = fee.plus(installment.getFeeChargesOutstanding(currency)); + penalty = penalty.plus(installment.getPenaltyChargesOutstanding(currency)); + isArrearsPresent = true; + } else if (installment.getFromDate().isBefore(paymentDate)) { + int totalPeriodDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays(); + int tillDays = Days.daysBetween(installment.getFromDate(), paymentDate).getDays(); + interest = interest.plus(calculateInterestForDays(totalPeriodDays, installment.getInterestOutstanding(currency) + .getAmount(), tillDays)); + for (LoanCharge loanCharge : this.charges) { + if (loanCharge.isActive() + && loanCharge.isDueForCollectionFromAndUpToAndIncluding(installment.getFromDate(), paymentDate)) { + if (loanCharge.isPenaltyCharge()) { + penalty = penalty.plus(loanCharge.getAmountOutstanding(currency)); + } else { + fee = fee.plus(loanCharge.getAmountOutstanding(currency)); + } + } + } + } + } + } + balances[0] = interest; + balances[1] = fee; + balances[2] = penalty; + return balances; + } + + private double calculateInterestForDays(int daysInPeriod, BigDecimal interest, int days) { + if (interest.doubleValue() == 0) { return 0; } + return ((interest.doubleValue()) / daysInPeriod) * days; + } + + public boolean canForecloseLoan() { + boolean canForecloseLoan = false; + if (isOpen()) { + canForecloseLoan = true; + } + return canForecloseLoan; + } + + public Money[] getReceivableIncome(final LocalDate tillDate) { + MonetaryCurrency currency = getCurrency(); + Money receivableInterest = Money.zero(currency); + Money receivableFee = Money.zero(currency); + Money receivablePenalty = Money.zero(currency); + Money[] receivables = new Money[3]; + for (final LoanTransaction transaction : this.loanTransactions) { + if (transaction.isNotReversed() && !transaction.isRepaymentAtDisbursement() && !transaction.isDisbursement() + && !transaction.getTransactionDate().isAfter(tillDate)) { + if (transaction.isAccrual()) { + receivableInterest = receivableInterest.plus(transaction.getInterestPortion(currency)); + receivableFee = receivableFee.plus(transaction.getFeeChargesPortion(currency)); + receivablePenalty = receivablePenalty.plus(transaction.getPenaltyChargesPortion(currency)); + } else if (transaction.isRepayment() || transaction.isChargePayment()) { + receivableInterest = receivableInterest.minus(transaction.getInterestPortion(currency)); + receivableFee = receivableFee.minus(transaction.getFeeChargesPortion(currency)); + receivablePenalty = receivablePenalty.minus(transaction.getPenaltyChargesPortion(currency)); + } + } + if (receivableInterest.isLessThanZero()) { + receivableInterest = receivableInterest.zero(); + } + if (receivableFee.isLessThanZero()) { + receivableFee = receivableFee.zero(); + } + if (receivablePenalty.isLessThanZero()) { + receivablePenalty = receivablePenalty.zero(); + } + } + receivables[0] = receivableInterest; + receivables[1] = receivableFee; + receivables[2] = receivablePenalty; + return receivables; + } + + public void handleForeClosureTransactions(final List<LoanTransaction> repaymentTransaction, + final LoanForeClosureDetailDTO foreClosureDetailDTO, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + + LoanEvent event = LoanEvent.LOAN_FORECLOSURE; + + validateAccountStatus(event); + + MonetaryCurrency currency = getCurrency(); + + final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory + .determineProcessor(this.transactionProcessingStrategy); + + loanRepaymentScheduleTransactionProcessor.processTransactionsFromDerivedFields(repaymentTransaction, currency, + this.repaymentScheduleInstallments, charges()); + this.loanTransactions.addAll(repaymentTransaction); + this.loanSubStatus = LoanSubStatus.FORECLOSED.getValue(); + updateLoanSummaryDerivedFields(); + doPostLoanTransactionChecks(foreClosureDetailDTO.getTransactionDate(), loanLifecycleStateMachine); + } + + public Money retrieveAccruedAmountAfterDate(final LocalDate tillDate) { + Money totalAmountAccrued = Money.zero(getCurrency()); + Money actualAmountTobeAccrued = Money.zero(getCurrency()); + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + totalAmountAccrued = totalAmountAccrued.plus(installment.getInterestAccrued(getCurrency())); + + if (tillDate.isAfter(installment.getFromDate()) && tillDate.isBefore(installment.getDueDate())) { + int daysInPeriod = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays(); + int tillDays = Days.daysBetween(installment.getFromDate(), tillDate).getDays(); + double interest = calculateInterestForDays(daysInPeriod, installment.getInterestCharged(getCurrency()).getAmount(), + tillDays); + actualAmountTobeAccrued = actualAmountTobeAccrued.plus(interest); + } else if ((tillDate.isAfter(installment.getFromDate()) && tillDate.isEqual(installment.getDueDate())) + || (tillDate.isEqual(installment.getFromDate()) && tillDate.isEqual(installment.getDueDate())) + || (tillDate.isAfter(installment.getFromDate()) && tillDate.isAfter(installment.getDueDate()))) { + actualAmountTobeAccrued = actualAmountTobeAccrued.plus(installment.getInterestAccrued(getCurrency())); + } + } + Money accredAmountAfterDate = totalAmountAccrued.minus(actualAmountTobeAccrued); + if (accredAmountAfterDate.isLessThanZero()) { + accredAmountAfterDate = Money.zero(getCurrency()); + } + return accredAmountAfterDate; + } + + public void validateForForeclosure(final LocalDate transactionDate) { + + if (isInterestRecalculationEnabledForProduct()) { + final String defaultUserMessage = "The loan with interest recalculation enabled cannot be foreclosed."; + throw new LoanForeclosureException("loan.with.interest.recalculation.enabled.cannot.be.foreclosured", defaultUserMessage, + getId()); + } + + LocalDate lastUserTransactionDate = getLastUserTransactionDate(); + + if (DateUtils.isDateInTheFuture(transactionDate)) { + final String defaultUserMessage = "The transactionDate cannot be in the future."; + throw new LoanForeclosureException("loan.foreclosure.transaction.date.is.in.future", defaultUserMessage, transactionDate); + } + + if (lastUserTransactionDate.isAfter(transactionDate)) { + final String defaultUserMessage = "The transactionDate cannot be in the future."; + throw new LoanForeclosureException("loan.foreclosure.transaction.date.cannot.before.the.last.transaction.date", + defaultUserMessage, transactionDate); + } + } + + public void updateInstallmentsPostDate(LocalDate transactionDate) { + List<LoanRepaymentScheduleInstallment> newInstallments = new ArrayList<>(this.repaymentScheduleInstallments); + final MonetaryCurrency currency = getCurrency(); + Money totalPrincipal = Money.zero(currency); + Money totalInterest = Money.zero(currency); + + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + if (installment.getDueDate().isAfter(transactionDate)) { + totalPrincipal = totalPrincipal.plus(installment.getPrincipalOutstanding(currency)); + totalInterest = totalInterest.plus(installment.getInterestOutstanding(currency)); + newInstallments.remove(installment); + } + } + + LoanRepaymentScheduleInstallment newInstallment = new LoanRepaymentScheduleInstallment(null, newInstallments.size() + 1, + newInstallments.get((newInstallments.size() - 1)).getDueDate(), transactionDate, totalPrincipal.getAmount(), + totalInterest.getAmount(), BigDecimal.ZERO, BigDecimal.ZERO, true, null); --- End diff -- Here the interest should be till that foreclosure date and also you need to check charges till that date. > Implementation of loan foreclosure functionality > ------------------------------------------------ > > Key: FINERACT-167 > URL: https://issues.apache.org/jira/browse/FINERACT-167 > Project: Apache Fineract > Issue Type: New Feature > Components: Loan > Reporter: Vishwanath R > Assignee: Markus Geiss > > On foreclosure of the active loan application, the following things should > happen, > 1) Repay total loan outstanding principal amount. > 2) Repay total Interest amount (accrued and due) till Foreclosure date. > 3) Repay total Fee, Charges or Penalties due, if any till Foreclosure date. > 4) Accounting entries will be made as per product configuration for interest > and loan portfolio payments. > 5) Future installment Interest, Fee, Charge, or Penalty, if any, is waived > off (there won’t be any accounting entries for these transactions). -- This message was sent by Atlassian JIRA (v6.3.4#6332)