temp check for null charges
This commit is contained in:
+175
-181
@@ -8,227 +8,221 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
|||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract implementation of {@link LoanRepaymentScheduleTransactionProcessor} which is more convenient for concrete implementations to extend.
|
* Abstract implementation of {@link LoanRepaymentScheduleTransactionProcessor}
|
||||||
|
* which is more convenient for concrete implementations to extend.
|
||||||
*
|
*
|
||||||
* @see HeavensFamilyLoanRepaymentScheduleTransactionProcessor
|
* @see HeavensFamilyLoanRepaymentScheduleTransactionProcessor
|
||||||
* @see CreocoreLoanRepaymentScheduleTransactionProcessor
|
* @see CreocoreLoanRepaymentScheduleTransactionProcessor
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor {
|
public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implements LoanRepaymentScheduleTransactionProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides support for passing all {@link LoanTransaction}'s so it will
|
* Provides support for passing all {@link LoanTransaction}'s so it will
|
||||||
* completely re-process the entire loan schedule. This is required in cases
|
* completely re-process the entire loan schedule. This is required in cases
|
||||||
* where the {@link LoanTransaction} being processed is in the past and
|
* where the {@link LoanTransaction} being processed is in the past and
|
||||||
* falls before existing transactions or and adjustment is made to an
|
* falls before existing transactions or and adjustment is made to an
|
||||||
* existing in which case the entire loan schedule needs to be re-processed.
|
* existing in which case the entire loan schedule needs to be re-processed.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void handleTransaction(
|
public void handleTransaction(final LocalDate disbursementDate, final List<LoanTransaction> transactionsPostDisbursement,
|
||||||
final LocalDate disbursementDate,
|
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
|
||||||
final List<LoanTransaction> transactionsPostDisbursement,
|
|
||||||
final MonetaryCurrency currency,
|
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
|
||||||
final Set<LoanCharge> charges) {
|
|
||||||
|
|
||||||
for (LoanCharge loanCharge : charges) {
|
if (charges != null) {
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
loanCharge.resetPaidAmount(currency);
|
if (!loanCharge.isDueAtDisbursement()) {
|
||||||
}
|
loanCharge.resetPaidAmount(currency);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
currentInstallment.resetDerivedComponents();
|
currentInstallment.resetDerivedComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-process loan charges over repayment periods (picking up on waived loan charges)
|
// re-process loan charges over repayment periods (picking up on waived
|
||||||
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
|
// loan charges)
|
||||||
wrapper.reprocess(currency, disbursementDate, installments, charges);
|
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
|
||||||
|
wrapper.reprocess(currency, disbursementDate, installments, charges);
|
||||||
|
|
||||||
for (LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
for (LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||||
|
|
||||||
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver()) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
handleTransaction(loanTransaction, currency, installments, charges);
|
handleTransaction(loanTransaction, currency, installments, charges);
|
||||||
} else if (loanTransaction.isWriteOff()) {
|
} else if (loanTransaction.isWriteOff()) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
handleWriteOff(loanTransaction, currency, installments);
|
handleWriteOff(loanTransaction, currency, installments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides support for processing the latest transaction (which should be latest transaction) against the loan schedule.
|
* Provides support for processing the latest transaction (which should be
|
||||||
*/
|
* latest transaction) against the loan schedule.
|
||||||
@Override
|
*/
|
||||||
public void handleTransaction(
|
@Override
|
||||||
final LoanTransaction loanTransaction,
|
public void handleTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
||||||
final MonetaryCurrency currency,
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
|
||||||
final Set<LoanCharge> charges) {
|
|
||||||
|
|
||||||
// find earliest unpaid installment for which to apply this transaction to.
|
// find earliest unpaid installment for which to apply this transaction
|
||||||
int installmentIndex = 0;
|
// to.
|
||||||
|
int installmentIndex = 0;
|
||||||
|
|
||||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
Money transactionAmountUnprocessed = loanTransaction.getAmount(currency);
|
Money transactionAmountUnprocessed = loanTransaction.getAmount(currency);
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
|
|
||||||
if (currentInstallment.isNotFullyCompleted()) {
|
if (currentInstallment.isNotFullyCompleted()) {
|
||||||
|
|
||||||
// is this transaction early/late/on-time with respect to the current installment?
|
// is this transaction early/late/on-time with respect to the
|
||||||
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate, transactionAmountUnprocessed)) {
|
// current installment?
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment, installments, loanTransaction, transactionDate, transactionAmountUnprocessed);
|
if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate, transactionAmountUnprocessed)) {
|
||||||
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, loanTransaction.getTransactionDate())) {
|
transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment, installments,
|
||||||
// does this result in a late payment of existing installment?
|
loanTransaction, transactionDate, transactionAmountUnprocessed);
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments, loanTransaction, transactionAmountUnprocessed);
|
} else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, loanTransaction.getTransactionDate())) {
|
||||||
} else {
|
// does this result in a late payment of existing
|
||||||
// standard transaction
|
// installment?
|
||||||
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed);
|
transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments,
|
||||||
}
|
loanTransaction, transactionAmountUnprocessed);
|
||||||
}
|
} else {
|
||||||
|
// standard transaction
|
||||||
|
transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction,
|
||||||
|
transactionAmountUnprocessed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
installmentIndex++;
|
installmentIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loanTransaction.isNotWaiver()) {
|
if (loanTransaction.isNotWaiver()) {
|
||||||
Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
|
Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
|
||||||
if (feeCharges.isGreaterThanZero()) {
|
if (feeCharges.isGreaterThanZero()) {
|
||||||
updateFeeChargesPaidAmountBy(feeCharges, charges);
|
updateFeeChargesPaidAmountBy(feeCharges, charges);
|
||||||
}
|
}
|
||||||
|
|
||||||
Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
|
Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
|
||||||
if (penaltyCharges.isGreaterThanZero()) {
|
if (penaltyCharges.isGreaterThanZero()) {
|
||||||
updatePenaltyChargesPaidAmountBy(penaltyCharges, charges);
|
updatePenaltyChargesPaidAmountBy(penaltyCharges, charges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionAmountUnprocessed.isGreaterThanZero()) {
|
if (transactionAmountUnprocessed.isGreaterThanZero()) {
|
||||||
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
onLoanOverpayment(loanTransaction, transactionAmountUnprocessed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFeeChargesPaidAmountBy(final Money feeCharges, final Set<LoanCharge> charges) {
|
private void updateFeeChargesPaidAmountBy(final Money feeCharges, final Set<LoanCharge> charges) {
|
||||||
|
|
||||||
Money amountRemaining = feeCharges;
|
Money amountRemaining = feeCharges;
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
if (!loanCharge.isDueAtDisbursement()) {
|
||||||
if (loanCharge.isFeeCharge() && loanCharge.isNotFullyPaid() && amountRemaining.isGreaterThanZero()) {
|
if (loanCharge.isFeeCharge() && loanCharge.isNotFullyPaid() && amountRemaining.isGreaterThanZero()) {
|
||||||
amountRemaining = loanCharge.updatePaidAmountBy(amountRemaining);
|
amountRemaining = loanCharge.updatePaidAmountBy(amountRemaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePenaltyChargesPaidAmountBy(final Money feeCharges, final Set<LoanCharge> charges) {
|
private void updatePenaltyChargesPaidAmountBy(final Money feeCharges, final Set<LoanCharge> charges) {
|
||||||
|
|
||||||
Money amountRemaining = feeCharges;
|
Money amountRemaining = feeCharges;
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
if (!loanCharge.isDueAtDisbursement()) {
|
||||||
|
|
||||||
if (loanCharge.isPenaltyCharge() && amountRemaining.isGreaterThanZero()) {
|
if (loanCharge.isPenaltyCharge() && amountRemaining.isGreaterThanZero()) {
|
||||||
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
||||||
amountRemaining = unpaidCharge.updatePaidAmountBy(amountRemaining);
|
amountRemaining = unpaidCharge.updatePaidAmountBy(amountRemaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(Set<LoanCharge> charges) {
|
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(Set<LoanCharge> charges) {
|
||||||
LoanCharge earliestUnpaidCharge = null;
|
LoanCharge earliestUnpaidCharge = null;
|
||||||
|
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) {
|
if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) {
|
||||||
if (earliestUnpaidCharge == null || loanCharge.getDueForCollectionAsOfLocalDate().isBefore(earliestUnpaidCharge.getDueForCollectionAsOfLocalDate())) {
|
if (earliestUnpaidCharge == null
|
||||||
earliestUnpaidCharge = loanCharge;
|
|| loanCharge.getDueForCollectionAsOfLocalDate().isBefore(earliestUnpaidCharge.getDueForCollectionAsOfLocalDate())) {
|
||||||
}
|
earliestUnpaidCharge = loanCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return earliestUnpaidCharge;
|
return earliestUnpaidCharge;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleWriteOff(
|
public void handleWriteOff(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
||||||
final LoanTransaction loanTransaction,
|
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
final MonetaryCurrency currency,
|
|
||||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
|
||||||
|
|
||||||
Money principalPortion = Money.zero(currency);
|
Money principalPortion = Money.zero(currency);
|
||||||
Money interestPortion = Money.zero(currency);
|
Money interestPortion = Money.zero(currency);
|
||||||
Money feeChargesPortion = Money.zero(currency);
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
Money penaltychargesPortion = Money.zero(currency);
|
Money penaltychargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
// determine how much is written off in total and breakdown for principal, interest and charges
|
// determine how much is written off in total and breakdown for
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
// principal, interest and charges
|
||||||
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
|
|
||||||
if (currentInstallment.isNotFullyCompleted()) {
|
if (currentInstallment.isNotFullyCompleted()) {
|
||||||
principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(currency));
|
principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(currency));
|
||||||
interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(currency));
|
interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(currency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// abstract interface
|
// abstract interface
|
||||||
/**
|
/**
|
||||||
* This method is responsible for checking if the current transaction is 'an
|
* This method is responsible for checking if the current transaction is 'an
|
||||||
* advance/early payment' based on the details passed through.
|
* advance/early payment' based on the details passed through.
|
||||||
*
|
*
|
||||||
* Default implementation simply processes transactions as 'Late' if the
|
* Default implementation simply processes transactions as 'Late' if the
|
||||||
* transaction date is after the installment due date.
|
* transaction date is after the installment due date.
|
||||||
*/
|
*/
|
||||||
protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex,
|
protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
|
||||||
final LocalDate transactionDate) {
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
|
LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
|
||||||
|
|
||||||
return transactionDate.isAfter(currentInstallment.getDueDate());
|
return transactionDate.isAfter(currentInstallment.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For late repayments, how should components of installment be paid off
|
* For late repayments, how should components of installment be paid off
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(
|
protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final Money transactionAmountUnprocessed);
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final Money transactionAmountUnprocessed);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is responsible for checking if the current transaction is 'an
|
* This method is responsible for checking if the current transaction is 'an
|
||||||
* advance/early payment' based on the details passed through.
|
* advance/early payment' based on the details passed through.
|
||||||
*/
|
*/
|
||||||
protected abstract boolean isTransactionInAdvanceOfInstallment(
|
protected abstract boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
|
||||||
final int currentInstallmentIndex,
|
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate, final Money transactionAmount);
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
|
||||||
final LocalDate transactionDate,
|
|
||||||
final Money transactionAmount);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For early/'in advance' repayments.
|
* For early/'in advance' repayments.
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final LoanRepaymentScheduleInstallment currentInstallment, final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final LoanTransaction loanTransaction, final LocalDate transactionDate, final Money paymentInAdvance);
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final LocalDate transactionDate,
|
|
||||||
final Money paymentInAdvance);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For normal on-time repayments.
|
* For normal on-time repayments.
|
||||||
*/
|
*/
|
||||||
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(
|
protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed);
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final Money transactionAmountUnprocessed);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a transaction results in an over-payment of the full loan.
|
* 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.
|
* transaction amount is greater than the total expected principal and
|
||||||
*/
|
* interest of the loan.
|
||||||
protected abstract void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount);
|
*/
|
||||||
|
protected abstract void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user