| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl; |
| |
| import java.util.List; |
| |
| import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; |
| import org.apache.fineract.organisation.monetary.domain.Money; |
| import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; |
| import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; |
| import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; |
| import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; |
| import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; |
| import org.joda.time.LocalDate; |
| |
| /** |
| * This {@link LoanRepaymentScheduleTransactionProcessor} defaults to having the |
| * payment order of Interest first, then principal, penalties and fees. |
| */ |
| public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor extends |
| AbstractLoanRepaymentScheduleTransactionProcessor { |
| |
| /** |
| * For early/'in advance' repayments, pay off in the same way as on-time |
| * payments, interest first, principal, penalties and charges. |
| */ |
| @SuppressWarnings("unused") |
| @Override |
| protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, |
| final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, |
| final LocalDate transactionDate, final Money paymentInAdvance, |
| List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) { |
| |
| return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); |
| } |
| |
| /** |
| * For late repayments, pay off in the same way as on-time payments, |
| * interest first then principal. |
| */ |
| @SuppressWarnings("unused") |
| @Override |
| protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, |
| final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction, |
| final Money transactionAmountUnprocessed, List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) { |
| |
| return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, |
| transactionMappings); |
| } |
| |
| /** |
| * For normal on-time repayments, pays off interest first, then principal. |
| */ |
| @Override |
| protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, |
| final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, |
| List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) { |
| |
| final LocalDate transactionDate = loanTransaction.getTransactionDate(); |
| final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); |
| Money transactionAmountRemaining = transactionAmountUnprocessed; |
| Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| |
| if (loanTransaction.isChargesWaiver()) { |
| penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate, |
| loanTransaction.getPenaltyChargesPortion(currency)); |
| transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); |
| |
| feeChargesPortion = currentInstallment |
| .waiveFeeChargesComponent(transactionDate, loanTransaction.getFeeChargesPortion(currency)); |
| transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); |
| |
| } else if (loanTransaction.isInterestWaiver()) { |
| interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); |
| |
| loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); |
| } else if (loanTransaction.isChargePayment()) { |
| if (loanTransaction.isPenaltyPayment()) { |
| penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); |
| } else { |
| feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); |
| } |
| loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); |
| } else { |
| interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); |
| |
| principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); |
| |
| penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); |
| |
| feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); |
| |
| loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); |
| } |
| if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { |
| transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, |
| interestPortion, feeChargesPortion, penaltyChargesPortion)); |
| } |
| return transactionAmountRemaining; |
| } |
| |
| @Override |
| protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, |
| final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, |
| List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) { |
| |
| final LocalDate transactionDate = loanTransaction.getTransactionDate(); |
| Money transactionAmountRemaining = transactionAmountUnprocessed; |
| Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); |
| |
| if (transactionAmountRemaining.isGreaterThanZero()) { |
| feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); |
| } |
| |
| if (transactionAmountRemaining.isGreaterThanZero()) { |
| penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); |
| } |
| |
| if (transactionAmountRemaining.isGreaterThanZero()) { |
| principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); |
| } |
| |
| if (transactionAmountRemaining.isGreaterThanZero()) { |
| interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining); |
| transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); |
| } |
| |
| loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); |
| if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { |
| transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, |
| interestPortion, feeChargesPortion, penaltyChargesPortion)); |
| } |
| return transactionAmountRemaining; |
| } |
| } |