From f09cf9a433e3445ea23df909b07e91181575f99d Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Wed, 29 Jan 2014 03:53:58 +0530 Subject: [PATCH] MIFOSX-888 - Early Repayments Strategy --- api-docs/apiLive.htm | 35 ++++- .../V141__add_early_repayment_strategy.sql | 3 + ...ntScheduleTransactionProcessorFactory.java | 5 + ...RepaymentScheduleTransactionProcessor.java | 138 ++++++++++++++++++ .../LoanTransactionProcessingStrategy.java | 4 + 5 files changed, 180 insertions(+), 5 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V141__add_early_repayment_strategy.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 087d6eece..1cdcf6d96 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -2503,7 +2503,7 @@ GET https://DomainName/api/v1/clients/{clientId}
clients?offset=10&limit=50

-
clients?orderBy=displayName,sortOrder=DESC
+
clients?orderBy=displayName&sortOrder=DESC
@@ -3663,7 +3663,7 @@ GET https://DomainName/mifosng-provider/api/v1/centers/9?associations=groupMembe
centers?offset=10&limit=50

-
centers?orderBy=name,sortOrder=DESC
+
centers?orderBy=name&sortOrder=DESC
@@ -4579,7 +4579,7 @@ DELETE groups/2
groups?offset=10&limit=50

-
groups?orderBy=name,sortOrder=DESC
+
groups?orderBy=name&sortOrder=DESC
@@ -4820,6 +4820,9 @@ An enumeration that indicates the type of transaction processing strategy to be
  • 4 = RBI (India)
  • 5 = Principal Interest Penalties Fees Order
  • 6 = Interest Principal Penalties Fees Order
  • +
  • 7 = Early Payment Strategy
  • + + @@ -5473,7 +5476,7 @@ GET https://DomainName/api/v1/loans/{loanId}
    loans?offset=10&limit=50

    -
    loans?orderBy=accountNo,sortOrder=DESC
    +
    loans?orderBy=accountNo&sortOrder=DESC
    @@ -8048,6 +8051,7 @@ An enumeration that indicates the type of transaction processing strategy to be
  • 4 = RBI (India)
  • 5 = Principal Interest Penalties Fees Order
  • 6 = Interest Principal Penalties Fees Order
  • +
  • 7 = Early Payment Strategy
  • @@ -20454,7 +20458,7 @@ No Request Body:
    journalentries?offset=10&limit=50

    -
    journalentries?orderBy=transactionId,sortOrder=DESC
    +
    journalentries?orderBy=transactionId&sortOrder=DESC

    journalentries?runningBalance=true

    @@ -22702,6 +22706,27 @@ No Request Body:

    On time payment: Any payment that pays off an installment that occurs exactly on the due date of the installment.

    Late payment: Any payment that pays off an installment that occurs after the due date of the installment. There is no penalization for late payment. The total cost of the loan does not automatically increase, no penalties are automatically applied to the loan.

    + +

    Early Payment Strategy

    +

    This strategy works similar to the Interest Principal Penalties Fees Order for on-time and late payments. If the installments are paid early we will

    +

    + Payment order: +

      +
    1. Overdue and due interest
    2. +
    3. Overdue and due principal
    4. +
    5. Overdue and due penalties
    6. +
    7. Overdue and due fees
    8. +
    +

    +

    Early payment: Any payment that pays off an installment that occurs before the due date of the installment. There is no incentive/advantage to making an early payment as theres no decrease in cost of the loan.

    + +

    On time payment: Any payment that pays off an installment that occurs exactly on the due date of the installment.

    + +

    Late payment: Any payment that pays off an installment that occurs after the due date of the installment. There is no penalization for late payment. The total cost of the loan does not automatically increase, no penalties are automatically applied to the loan.

    + + + +
    diff --git a/mifosng-db/migrations/core_db/V141__add_early_repayment_strategy.sql b/mifosng-db/migrations/core_db/V141__add_early_repayment_strategy.sql new file mode 100644 index 000000000..1039bfab4 --- /dev/null +++ b/mifosng-db/migrations/core_db/V141__add_early_repayment_strategy.sql @@ -0,0 +1,3 @@ +INSERT INTO `ref_loan_transaction_processing_strategy` ( `code`, `name`) +VALUES + ( 'early-repayment-strategy', 'Early Repayment Strategy'); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessorFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessorFactory.java index aaf44ca01..ab565481f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessorFactory.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleTransactionProcessorFactory.java @@ -12,6 +12,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl. import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.MifosStyleLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor; +import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; import org.springframework.stereotype.Component; @@ -32,6 +33,10 @@ public class LoanRepaymentScheduleTransactionProcessorFactory { if (transactionProcessingStrategy.isHeavensfamilyStrategy()) { processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor(); } + + if (transactionProcessingStrategy.isEarlyPaymentStrategy()) { + processor = new EarlyPaymentLoanRepaymentScheduleTransactionProcessor(); + } if (transactionProcessingStrategy.isCreocoreStrategy()) { processor = new CreocoreLoanRepaymentScheduleTransactionProcessor(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java new file mode 100644 index 000000000..bc953a5d9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java @@ -0,0 +1,138 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl; + +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; +import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; + +/** + * This {@link LoanRepaymentScheduleTransactionProcessor} defaults to having the + * payment order of Interest first, then principal, penalties and fees. + */ +public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends + AbstractLoanRepaymentScheduleTransactionProcessor { + + /** + * For early/'in advance' repayments, pay off in the same way as on-time + * payments, interest first, principal, penalties and charges. + */ + @Override + protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, + final List installments, final LoanTransaction loanTransaction, + final LocalDate transactionDate, final Money paymentInAdvance) { + + + final MonetaryCurrency currency = paymentInAdvance.getCurrency(); + Money transactionAmountRemaining = paymentInAdvance; + 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()) { + // zero this type of transaction and ignore it for now. + transactionAmountRemaining = Money.zero(currency); + } 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 { + + // Only allocate to principal: + principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining); + transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); + + loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + } + + return transactionAmountRemaining; + + + } + + /** + * 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 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) { + + 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()) { + // zero this type of transaction and ignore it for now. + transactionAmountRemaining = Money.zero(currency); + } 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); + } + + return transactionAmountRemaining; + } + + + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanTransactionProcessingStrategy.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanTransactionProcessingStrategy.java index 76377e836..3aa255a51 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanTransactionProcessingStrategy.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanTransactionProcessingStrategy.java @@ -37,6 +37,10 @@ public class LoanTransactionProcessingStrategy extends AbstractPersistable public boolean isHeavensfamilyStrategy() { return "heavensfamily-strategy".equalsIgnoreCase(this.code); } + + public boolean isEarlyPaymentStrategy() { + return "early-payment-strategy".equalsIgnoreCase(this.code); + } public boolean isCreocoreStrategy() { return "creocore-strategy".equalsIgnoreCase(this.code);