mifosx-90: validation of loan and loan charges state when adding and removing loan charges and when creating, modifying loan applications.

This commit is contained in:
Keith Woodlock
2012-11-06 15:12:16 +00:00
parent 81f7b614b4
commit b185bc5f5b
6 changed files with 59 additions and 11 deletions
@@ -88,6 +88,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
} else {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
@@ -90,6 +90,12 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
}
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
@@ -120,7 +126,12 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
} else {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
@@ -261,6 +261,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public void addLoanCharge(final LoanCharge loanCharge) {
validateLoanIsNotClosed(loanCharge);
if (isDisbursed() && loanCharge.isDueAtDisbursement()) {
// Note: added this constraint to restrict adding charges to a loan after it is disbursed
// if the loan charge payment type is 'Disbursement'.
@@ -272,10 +274,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
throw new LoanChargeCannotBeAddedException("loanCharge", "due.at.disbursement.and.loan.is.disbursed", defaultUserMessage, getId(), loanCharge.name());
}
if (loanCharge.isSpecifiedDueDate() && !loanCharge.isDueForCollectionBetween(getDisbursementDate(), getLastRepaymentPeriodDueDate())) {
final String defaultUserMessage = "This charge which is due at disbursement cannot be added as the loan is already disbursed.";
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage, getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
}
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
loanCharge.update(this);
this.charges.add(loanCharge);
@@ -293,12 +292,29 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
loanRepaymentScheduleTransactionProcessor.handleTransaction(allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, this.charges);
}
}
private void validateLoanIsNotClosed(final LoanCharge loanCharge) {
if (isClosed()) {
final String defaultUserMessage = "This charge cannot be added as the loan is already closed.";
throw new LoanChargeCannotBeAddedException("loanCharge", "loan.is.closed", defaultUserMessage, getId(), loanCharge.name());
}
}
private void validateChargeHasValidSpecifiedDateIfApplicable(final LoanCharge loanCharge, final LocalDate disbursementDate, final LocalDate lastRepaymentPeriodDueDate) {
if (loanCharge.isSpecifiedDueDate() && !loanCharge.isDueForCollectionBetween(disbursementDate, lastRepaymentPeriodDueDate)) {
final String defaultUserMessage = "This charge which is due at disbursement cannot be added as the loan is already disbursed.";
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage, getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
}
}
private LocalDate getLastRepaymentPeriodDueDate() {
return this.repaymentScheduleInstallments.get(this.repaymentScheduleInstallments.size()-1).getDueDate();
}
public void removeLoanCharge(final LoanCharge loanCharge) {
validateLoanIsNotClosed(loanCharge);
boolean removed = this.charges.remove(loanCharge);
if (removed) {
updateTotalChargesDueAtDisbursement();
@@ -531,6 +547,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
"cannot.be.after.expected.disbursement.date", errorMessage,
submittedOn, getExpectedDisbursedOnLocalDate());
}
for (LoanCharge loanCharge : this.charges) {
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
}
}
public void reject(final LocalDate rejectedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -25,7 +25,7 @@ public class LoanRepaymentScheduleTransactionProcessorFactory {
}
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
processor = new AdhikarLoanRepaymentScheduleTransactionProcessor();
processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
}
@@ -7,7 +7,7 @@ import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
/**
* Adhikar style {@link LoanRepaymentScheduleTransactionProcessor}.
* Adhikar/RBI style {@link LoanRepaymentScheduleTransactionProcessor}.
*
* From https://mifosforge.jira.com/browse/MIFOS-5636:
*
@@ -19,7 +19,7 @@ import org.mifosng.platform.currency.domain.Money;
* 20 Payment to interest on Installment #1 (200 principal remaining)
* 20 Payment to interest on Installment #2 (200 principal remaining)
*/
public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
/**
* For creocore, early is defined as any date before the installment due
@@ -83,7 +83,13 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
if (
installment.isInterestDue(currency) &&
(installment.isOverdueOn(loanTransaction.getTransactionDate()) || installment.getInstallmentNumber().equals(currentInstallmentBasedOnTransactionDate.getInstallmentNumber()))
) {
) {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
Money interestPortion = installment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
@@ -139,6 +145,12 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
} else {
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
@@ -18,7 +18,7 @@ import org.mockito.runners.MockitoJUnitRunner;
public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
// class under test
private AdhikarLoanRepaymentScheduleTransactionProcessor processor;
private RBILoanRepaymentScheduleTransactionProcessor processor;
//
private LocalDate july2nd = new LocalDate(2012, 7, 2);
@@ -30,7 +30,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars);
processor = new AdhikarLoanRepaymentScheduleTransactionProcessor();
processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
/**