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:
+5
@@ -88,6 +88,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
|
|||||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
} else {
|
} else {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|||||||
+11
@@ -90,6 +90,12 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
@@ -120,6 +126,11 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
} else {
|
} else {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|||||||
@@ -261,6 +261,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
public void addLoanCharge(final LoanCharge loanCharge) {
|
public void addLoanCharge(final LoanCharge loanCharge) {
|
||||||
|
|
||||||
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
if (isDisbursed() && loanCharge.isDueAtDisbursement()) {
|
if (isDisbursed() && loanCharge.isDueAtDisbursement()) {
|
||||||
// Note: added this constraint to restrict adding charges to a loan after it is disbursed
|
// Note: added this constraint to restrict adding charges to a loan after it is disbursed
|
||||||
// if the loan charge payment type is 'Disbursement'.
|
// 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());
|
throw new LoanChargeCannotBeAddedException("loanCharge", "due.at.disbursement.and.loan.is.disbursed", defaultUserMessage, getId(), loanCharge.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loanCharge.isSpecifiedDueDate() && !loanCharge.isDueForCollectionBetween(getDisbursementDate(), getLastRepaymentPeriodDueDate())) {
|
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
loanCharge.update(this);
|
loanCharge.update(this);
|
||||||
this.charges.add(loanCharge);
|
this.charges.add(loanCharge);
|
||||||
@@ -294,11 +293,28 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
private LocalDate getLastRepaymentPeriodDueDate() {
|
||||||
return this.repaymentScheduleInstallments.get(this.repaymentScheduleInstallments.size()-1).getDueDate();
|
return this.repaymentScheduleInstallments.get(this.repaymentScheduleInstallments.size()-1).getDueDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLoanCharge(final LoanCharge loanCharge) {
|
public void removeLoanCharge(final LoanCharge loanCharge) {
|
||||||
|
|
||||||
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
boolean removed = this.charges.remove(loanCharge);
|
boolean removed = this.charges.remove(loanCharge);
|
||||||
if (removed) {
|
if (removed) {
|
||||||
updateTotalChargesDueAtDisbursement();
|
updateTotalChargesDueAtDisbursement();
|
||||||
@@ -531,6 +547,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
"cannot.be.after.expected.disbursement.date", errorMessage,
|
"cannot.be.after.expected.disbursement.date", errorMessage,
|
||||||
submittedOn, getExpectedDisbursedOnLocalDate());
|
submittedOn, getExpectedDisbursedOnLocalDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (LoanCharge loanCharge : this.charges) {
|
||||||
|
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reject(final LocalDate rejectedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void reject(final LocalDate rejectedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ public class LoanRepaymentScheduleTransactionProcessorFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
|
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
|
||||||
processor = new AdhikarLoanRepaymentScheduleTransactionProcessor();
|
processor = new RBILoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-2
@@ -7,7 +7,7 @@ import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
|||||||
import org.mifosng.platform.currency.domain.Money;
|
import org.mifosng.platform.currency.domain.Money;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adhikar style {@link LoanRepaymentScheduleTransactionProcessor}.
|
* Adhikar/RBI style {@link LoanRepaymentScheduleTransactionProcessor}.
|
||||||
*
|
*
|
||||||
* From https://mifosforge.jira.com/browse/MIFOS-5636:
|
* 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 #1 (200 principal remaining)
|
||||||
* 20 Payment to interest on Installment #2 (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
|
* For creocore, early is defined as any date before the installment due
|
||||||
@@ -84,6 +84,12 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
|
|||||||
installment.isInterestDue(currency) &&
|
installment.isInterestDue(currency) &&
|
||||||
(installment.isOverdueOn(loanTransaction.getTransactionDate()) || installment.getInstallmentNumber().equals(currentInstallmentBasedOnTransactionDate.getInstallmentNumber()))
|
(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);
|
Money interestPortion = installment.payInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
@@ -140,6 +146,12 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
|
|||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
+2
-2
@@ -18,7 +18,7 @@ import org.mockito.runners.MockitoJUnitRunner;
|
|||||||
public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
|
public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
|
||||||
|
|
||||||
// class under test
|
// class under test
|
||||||
private AdhikarLoanRepaymentScheduleTransactionProcessor processor;
|
private RBILoanRepaymentScheduleTransactionProcessor processor;
|
||||||
|
|
||||||
//
|
//
|
||||||
private LocalDate july2nd = new LocalDate(2012, 7, 2);
|
private LocalDate july2nd = new LocalDate(2012, 7, 2);
|
||||||
@@ -30,7 +30,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessorTest {
|
|||||||
|
|
||||||
installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars);
|
installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(july2nd, usDollars);
|
||||||
|
|
||||||
processor = new AdhikarLoanRepaymentScheduleTransactionProcessor();
|
processor = new RBILoanRepaymentScheduleTransactionProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user