create two types of repayment strategy from abstract template.
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link LoanRepaymentScheduleTransactionProcessor} which is more convenient for concrete implementations to extend.
|
||||
*
|
||||
* @see HeavensFamilyLoanRepaymentScheduleTransactionProcessor
|
||||
* @see CreocoreLoanRepaymentScheduleTransactionProcessor
|
||||
*/
|
||||
public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor {
|
||||
|
||||
/**
|
||||
* Provides support for passing all {@link LoanTransaction}'s so it will
|
||||
* completely re-process the entire loan schedule. This is required in cases
|
||||
* where the {@link LoanTransaction} being processed is in the past and
|
||||
* falls before existing transactions or and adjustment is made to an
|
||||
* existing in which case the entire loan schedule needs to be re-processed.
|
||||
*/
|
||||
@Override
|
||||
public void handleTransaction(
|
||||
final List<LoanTransaction> repaymentsOrWaivers,
|
||||
final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
|
||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
currentInstallment.resetDerivedComponents();
|
||||
}
|
||||
|
||||
for (LoanTransaction loanTransaction : repaymentsOrWaivers) {
|
||||
|
||||
loanTransaction.resetDerivedComponents();
|
||||
handleTransaction(loanTransaction, currency, installments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides support for processing the latest transaction (which should be latest transaction) against the loan schedule.
|
||||
*/
|
||||
@Override
|
||||
public void handleTransaction(
|
||||
final LoanTransaction loanTransaction,
|
||||
final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
|
||||
// find earliest unpaid installment for which to apply this transaction to.
|
||||
int installmentIndex = 0;
|
||||
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
Money transactionAmountUnprocessed = loanTransaction.getAmount(currency);
|
||||
|
||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
|
||||
if (currentInstallment.isNotFullyCompleted()) {
|
||||
|
||||
// is this transaction early/late/on-time with respect to the current installment?
|
||||
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate, transactionAmountUnprocessed)) {
|
||||
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment, installments, loanTransaction, transactionDate, transactionAmountUnprocessed);
|
||||
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, loanTransaction.getTransactionDate())) {
|
||||
// does this result in a late payment of existing installment?
|
||||
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments, loanTransaction, transactionAmountUnprocessed);
|
||||
} else {
|
||||
// standard transaction
|
||||
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed);
|
||||
}
|
||||
}
|
||||
|
||||
installmentIndex++;
|
||||
}
|
||||
|
||||
if (transactionAmountUnprocessed.isGreaterThanZero()) {
|
||||
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
||||
}
|
||||
}
|
||||
|
||||
// abstract interface
|
||||
/**
|
||||
* This method is responsible for checking if the current transaction is 'an
|
||||
* advance/early payment' based on the details passed through.
|
||||
*
|
||||
* Default implementation simply processes transactions as 'Late' if the
|
||||
* transaction date is after the installment due date.
|
||||
*/
|
||||
protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LocalDate transactionDate) {
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
|
||||
|
||||
return transactionDate.isAfter(currentInstallment.getDueDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* For late repayments, how should components of installment be paid off
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed);
|
||||
|
||||
/**
|
||||
* This method is responsible for checking if the current transaction is 'an
|
||||
* advance/early payment' based on the details passed through.
|
||||
*/
|
||||
protected abstract boolean isTransactionInAdvanceOfInstallment(
|
||||
final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LocalDate transactionDate,
|
||||
final Money transactionAmount);
|
||||
|
||||
/**
|
||||
* For early/'in advance' repayments.
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate,
|
||||
final Money paymentInAdvance);
|
||||
|
||||
/**
|
||||
* For normal on-time repayments.
|
||||
*/
|
||||
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed);
|
||||
|
||||
/**
|
||||
* Invoked when a transaction results in an over-payment of the full loan.
|
||||
*
|
||||
* transaction amount is greater than the total expected principal and interest of the loan.
|
||||
*/
|
||||
protected abstract void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount);
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
|
||||
/**
|
||||
* Creocore style {@link LoanRepaymentScheduleTransactionProcessor}.
|
||||
*
|
||||
* For standard transactions, pays off components in order of interest, then
|
||||
* principal.
|
||||
*
|
||||
* If a transaction results in an advance payment or over-payment for a given
|
||||
* installment, the over paid amount is pay off on the principal component of
|
||||
* subsequent installments.
|
||||
*
|
||||
* If the entire principal of an installment is paid in advance then the
|
||||
* interest component is waived.
|
||||
*/
|
||||
public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
|
||||
AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||
|
||||
/**
|
||||
* For creocore, early is defined as any date before the installment due
|
||||
* date
|
||||
*/
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(
|
||||
final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LocalDate transactionDate, final Money transactionAmount) {
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||
|
||||
return transactionDate.isBefore(currentInstallment.getDueDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* For early/'in advance' repayments, pays off principal component only.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance);
|
||||
}
|
||||
|
||||
/**
|
||||
* For late repayments, pay off in the same way as on-time payments,
|
||||
* interest first then principal.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(
|
||||
currentInstallment, loanTransaction,
|
||||
transactionAmountUnprocessed);
|
||||
}
|
||||
|
||||
/**
|
||||
* For normal on-time repayments, pays off interest first, then principal.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment
|
||||
.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining
|
||||
.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion,
|
||||
interestWaivedPortion);
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction,
|
||||
final Money loanOverPaymentAmount) {
|
||||
// dont do anything for with loan over-payment
|
||||
}
|
||||
}
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
|
||||
/**
|
||||
* Heavensfamily style {@link LoanRepaymentScheduleTransactionProcessor}.
|
||||
*
|
||||
* For standard transactions, pays off components in order of interest, then principal.
|
||||
*
|
||||
* If a transaction results in an overpayment for a given installment, the over paid amount is pay off on the principal component of subsequent installments.
|
||||
*/
|
||||
public class DefaultLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor {
|
||||
|
||||
@Override
|
||||
public void handleTransaction(final List<LoanTransaction> repaymentsOrWaivers,
|
||||
final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
|
||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
currentInstallment.resetDerivedComponents();
|
||||
}
|
||||
|
||||
for (LoanTransaction loanTransaction : repaymentsOrWaivers) {
|
||||
|
||||
loanTransaction.resetDerivedComponents();
|
||||
handleTransaction(loanTransaction, currency, installments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments) {
|
||||
|
||||
// find earliest unpaid installment for which to apply this transaction to.
|
||||
boolean transactionProcessed = false;
|
||||
int installmentIndex = 0;
|
||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
|
||||
if (currentInstallment.isNotFullyCompleted() && !transactionProcessed) {
|
||||
|
||||
if (isTransactionInAdvance(installmentIndex, installments, loanTransaction)) {
|
||||
handleTransactionInAdvance(installments, loanTransaction, currency);
|
||||
transactionProcessed = true;
|
||||
} else if (isTransactionLate(installmentIndex, installments, loanTransaction.getTransactionDate())) {
|
||||
// does this result in a late payment of existing installment?
|
||||
handleLateTransaction(currentInstallment, installments, loanTransaction, currency);
|
||||
transactionProcessed = true;
|
||||
} else {
|
||||
// standard transaction
|
||||
Money installmentOverPaymentAmount = handleOnTimeTransaction(currentInstallment, loanTransaction, currency);
|
||||
Money loanOverPaymentAmount = handleOverpaymentTransaction(installments, loanTransaction, currency, installmentOverPaymentAmount);
|
||||
if (loanOverPaymentAmount.isGreaterThanZero()) {
|
||||
onLoanOverpayment(loanTransaction, currency, loanOverPaymentAmount);
|
||||
}
|
||||
transactionProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
installmentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dont do anything special for late payments e.g. no fees, charges or penalties are applied
|
||||
* @param currentInstallment2
|
||||
*/
|
||||
protected void handleLateTransaction(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final MonetaryCurrency currency) {
|
||||
|
||||
Money installmentOverPaymentAmount = handleOnTimeTransaction(currentInstallment, loanTransaction, currency);
|
||||
Money loanOverPaymentAmount = handleOverpaymentTransaction(installments, loanTransaction, currency, installmentOverPaymentAmount);
|
||||
if (loanOverPaymentAmount.isGreaterThanZero()) {
|
||||
onLoanOverpayment(loanTransaction, currency, loanOverPaymentAmount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* just process transactions paid in advance the same as you would handle an overpayment.
|
||||
*/
|
||||
protected void handleTransactionInAdvance(
|
||||
List<LoanRepaymentScheduleInstallment> installments,
|
||||
LoanTransaction loanTransaction, MonetaryCurrency currency) {
|
||||
|
||||
final Money paymentInAdvance = loanTransaction.getAmount(currency);
|
||||
Money loanOverPaymentAmount = handleOverpaymentTransaction(installments, loanTransaction, currency, paymentInAdvance);
|
||||
if (loanOverPaymentAmount.isGreaterThanZero()) {
|
||||
onLoanOverpayment(loanTransaction, currency, loanOverPaymentAmount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* called when a transaction results in a overpayment of the full loan i.e.
|
||||
* transaction amount is greater than the total expected principal +
|
||||
* interest of the loan.
|
||||
*/
|
||||
protected void onLoanOverpayment(@SuppressWarnings("unused") final LoanTransaction loanTransaction, @SuppressWarnings("unused") final MonetaryCurrency currency,
|
||||
@SuppressWarnings("unused") final Money loanOverPaymentAmount) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles overpayment scenario by paying of the principal of the next/subsequent installments that are not fully completed.
|
||||
*/
|
||||
protected Money handleOverpaymentTransaction(
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||
final Money overPaymentAmount) {
|
||||
|
||||
Money transactionAmountRemaining = overPaymentAmount;
|
||||
Money principalPortion = Money.zero(currency);
|
||||
Money interestPortion = Money.zero(currency);
|
||||
Money interestWaivedPortion = Money.zero(currency);
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.isPrincipalNotCompleted(currency) && transactionAmountRemaining.isGreaterThanZero()) {
|
||||
principalPortion = installment.payPrincipalComponent(transactionAmountRemaining);
|
||||
if (installment.isPrincipalCompleted(currency)) {
|
||||
interestWaivedPortion = installment.waiveInterestComponent(installment.getInterest(currency));
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion);
|
||||
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
// reset portions for next installment
|
||||
principalPortion = Money.zero(currency);
|
||||
interestPortion = Money.zero(currency);
|
||||
interestWaivedPortion = Money.zero(currency);
|
||||
}
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected Money handleOnTimeTransaction(final LoanRepaymentScheduleInstallment installment, final LoanTransaction loanTransaction, MonetaryCurrency currency) {
|
||||
|
||||
Money transactionAmountRemaining = loanTransaction.getAmount(currency);
|
||||
|
||||
Money interestPortion = installment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
Money principalPortion = installment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion);
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is overpayment if the amount in the transactions is greater than amount due.
|
||||
*/
|
||||
protected boolean isTransactionOverpayment(final int installmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Money transactionAmount) {
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
|
||||
|
||||
return transactionAmount.isGreaterThan(currentInstallment.getTotalDue(transactionAmount.getCurrency()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is late if the date of the transactions is after that of the installment due date.
|
||||
*/
|
||||
protected boolean isTransactionLate(final int installmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LocalDate transactionDate) {
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
|
||||
|
||||
return transactionDate.isAfter(currentInstallment.getDueDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* is in advance if the transaction date is before or equal to last installment due date (in other words not after last installment date)
|
||||
*/
|
||||
protected boolean isTransactionInAdvance(final int installmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction transaction) {
|
||||
|
||||
boolean isInAdvance = false;
|
||||
|
||||
LocalDate lastInstallmentDueDate = null;
|
||||
if (installmentIndex > 0) {
|
||||
int previousInstallmentIndex = installmentIndex - 1;
|
||||
LoanRepaymentScheduleInstallment previousInstallment = installments.get(previousInstallmentIndex);
|
||||
lastInstallmentDueDate = previousInstallment.getDueDate();
|
||||
|
||||
return !transaction.getTransactionDate().isAfter(lastInstallmentDueDate);
|
||||
}
|
||||
|
||||
return isInAdvance;
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
|
||||
/**
|
||||
* Heavensfamily style {@link LoanRepaymentScheduleTransactionProcessor}.
|
||||
*
|
||||
* For standard transactions, pays off components in order of interest, then
|
||||
* principal.
|
||||
*
|
||||
* If a transaction results in an advance payment or overpayment for a given
|
||||
* installment, the over paid amount is pay off on the principal component of
|
||||
* subsequent installments.
|
||||
*
|
||||
* If the entire principal of an installment is paid in advance then the interest component is waived.
|
||||
*/
|
||||
public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||
|
||||
/**
|
||||
* For late repayments, pay off in the same way as on-time payments,
|
||||
* interest first then principal.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTransactionInAdvanceOfInstallment(
|
||||
final int currentInstallmentIndex,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LocalDate transactionDate,
|
||||
final Money transactionAmount) {
|
||||
|
||||
boolean isInAdvance = false;
|
||||
|
||||
LocalDate lastInstallmentDueDate = null;
|
||||
if (currentInstallmentIndex > 0) {
|
||||
int previousInstallmentIndex = currentInstallmentIndex - 1;
|
||||
LoanRepaymentScheduleInstallment previousInstallment = installments.get(previousInstallmentIndex);
|
||||
lastInstallmentDueDate = previousInstallment.getDueDate();
|
||||
|
||||
return !transactionDate.isAfter(lastInstallmentDueDate);
|
||||
}
|
||||
|
||||
return isInAdvance;
|
||||
}
|
||||
|
||||
/**
|
||||
* For early/'in advance' repayments, pays off principal component only.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||
|
||||
Money transactionAmountRemaining = paymentInAdvance;
|
||||
MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||
|
||||
// placeholders for tracking the breakdown of components relevant to
|
||||
// this transaction.
|
||||
Money principalPortion = Money.zero(currency);
|
||||
Money interestPortion = Money.zero(currency);
|
||||
Money interestWaivedPortion = Money.zero(currency);
|
||||
|
||||
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
||||
principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
if (currentInstallment.isPrincipalCompleted(currency)) {
|
||||
interestWaivedPortion = currentInstallment
|
||||
.waiveInterestComponent(currentInstallment
|
||||
.getInterest(currency));
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion,
|
||||
interestWaivedPortion);
|
||||
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
}
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* For normal on-time repayments, pays off interest first, then principal.
|
||||
*/
|
||||
@Override
|
||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment
|
||||
.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining
|
||||
.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion,
|
||||
interestWaivedPortion);
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoanOverpayment(
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money loanOverPaymentAmount) {
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@Transient
|
||||
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
|
||||
|
||||
@Transient
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
|
||||
|
||||
public static Loan createNew(final Fund fund,
|
||||
final LoanProduct loanProduct, final Client client,
|
||||
@@ -441,7 +444,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
|
||||
Collections.sort(repaymentsOrWaivers, transactionComparator);
|
||||
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = new DefaultLoanRepaymentScheduleTransactionProcessor();
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor();
|
||||
if (isTransactionChronologicallyLatest && !adjusted) {
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments);
|
||||
} else {
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import org.mifosng.platform.infrastructure.MifosPlatformTenant;
|
||||
import org.mifosng.platform.infrastructure.ThreadLocalContextUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LoanRepaymentScheduleTransactionProcessorFactory {
|
||||
|
||||
public LoanRepaymentScheduleTransactionProcessor determineProcessor() {
|
||||
|
||||
LoanRepaymentScheduleTransactionProcessor processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||
|
||||
// FIXME - Just temp way of deciding what 'repayment strategy' to use for now.
|
||||
MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
|
||||
if ("mifostenant-creocore".equalsIgnoreCase(tenant.getSchemaName())) {
|
||||
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user