MIFOSX-376: track when an installment is completed in full.
This commit is contained in:
+5
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
ADD COLUMN `obligations_met_on_date` DATE NULL DEFAULT NULL AFTER `completed_derived`;
|
||||||
|
|
||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
CHANGE COLUMN `interest_waived_derived` `interest_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `interest_writtenoff_derived` ;
|
||||||
+9
-6
@@ -43,6 +43,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
currentInstallment.resetDerivedComponents();
|
currentInstallment.resetDerivedComponents();
|
||||||
|
currentInstallment.updateDerivedFields(currency, disbursementDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-process loan charges over repayment periods (picking up on waived
|
// re-process loan charges over repayment periods (picking up on waived
|
||||||
@@ -108,7 +109,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
|
|
||||||
if (currentInstallment.isNotFullyCompleted()) {
|
if (currentInstallment.isNotFullyPaidOff()) {
|
||||||
|
|
||||||
// is this transaction early/late/on-time with respect to the
|
// is this transaction early/late/on-time with respect to the
|
||||||
// current installment?
|
// current installment?
|
||||||
@@ -205,6 +206,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
public void handleWriteOff(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
public void handleWriteOff(final LoanTransaction loanTransaction, final MonetaryCurrency currency,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments) {
|
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
|
|
||||||
|
LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
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);
|
||||||
@@ -214,11 +216,12 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
// principal, interest and charges
|
// principal, interest and charges
|
||||||
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
|
|
||||||
if (currentInstallment.isNotFullyCompleted()) {
|
if (currentInstallment.isNotFullyPaidOff()) {
|
||||||
principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(currency));
|
principalPortion = principalPortion.plus(currentInstallment.writeOffOutstandingPrincipal(transactionDate, currency));
|
||||||
interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(currency));
|
interestPortion = interestPortion.plus(currentInstallment.writeOffOutstandingInterest(transactionDate, currency));
|
||||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.writeOffOutstandingFeeCharges(currency));
|
feeChargesPortion = feeChargesPortion.plus(currentInstallment.writeOffOutstandingFeeCharges(transactionDate, currency));
|
||||||
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.writeOffOutstandingPenaltyCharges(currency));
|
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.writeOffOutstandingPenaltyCharges(transactionDate,
|
||||||
|
currency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+83
-92
@@ -24,105 +24,96 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
* If the entire principal of an installment is paid in advance then the
|
* If the entire principal of an installment is paid in advance then the
|
||||||
* interest component is waived.
|
* interest component is waived.
|
||||||
*/
|
*/
|
||||||
public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
|
public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||||
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
|
||||||
* date
|
* date
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(
|
protected 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) {
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
return transactionDate.isBefore(currentInstallment.getDueDate());
|
return transactionDate.isBefore(currentInstallment.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For early/'in advance' repayments, pay off in the same way as on-time payments,
|
* For early/'in advance' repayments, pay off in the same way as on-time
|
||||||
* interest first then principal.
|
* payments, interest first then principal.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance);
|
||||||
* For late repayments, pay off in the same way as on-time payments,
|
}
|
||||||
* interest first then principal.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Override
|
|
||||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(
|
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final Money transactionAmountUnprocessed) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(
|
/**
|
||||||
currentInstallment, loanTransaction,
|
* For late repayments, pay off in the same way as on-time payments,
|
||||||
transactionAmountUnprocessed);
|
* interest first then principal.
|
||||||
}
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@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) {
|
|
||||||
|
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
/**
|
||||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
* For normal on-time repayments, pays off interest first, then principal.
|
||||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
*/
|
||||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
@Override
|
||||||
Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) {
|
||||||
|
|
||||||
if (loanTransaction.isChargesWaiver()) {
|
|
||||||
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(loanTransaction.getPenaltyChargesPortion(currency));
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
|
||||||
|
|
||||||
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(loanTransaction.getFeeChargesPortion(currency));
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
|
||||||
|
|
||||||
} else if (loanTransaction.isInterestWaiver()) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
@Override
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction,
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
final Money loanOverPaymentAmount) {
|
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
// dont do anything for with loan over-payment
|
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
}
|
Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
|
Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
|
|
||||||
|
if (loanTransaction.isChargesWaiver()) {
|
||||||
|
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment
|
||||||
|
.waiveFeeChargesComponent(transactionDate, loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
} else if (loanTransaction.isInterestWaiver()) {
|
||||||
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
} else {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Override
|
||||||
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
|
// dont do anything for with loan over-payment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+25
-21
@@ -55,7 +55,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
lastInstallmentDueDate = previousInstallment.getDueDate();
|
lastInstallmentDueDate = previousInstallment.getDueDate();
|
||||||
|
|
||||||
isInAdvance = !(transactionDate.isAfter(lastInstallmentDueDate) || (transactionDate.isEqual(lastInstallmentDueDate)));
|
isInAdvance = !(transactionDate.isAfter(lastInstallmentDueDate) || (transactionDate.isEqual(lastInstallmentDueDate)));
|
||||||
|
|
||||||
return isInAdvance;
|
return isInAdvance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,16 +75,16 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
Money penaltyChargesPortion = Money.zero(currency);
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
|
|
||||||
if (loanTransaction.isInterestWaiver()) {
|
if (loanTransaction.isInterestWaiver()) {
|
||||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
if (currentInstallment.isPrincipalCompleted(currency)) {
|
if (currentInstallment.isPrincipalCompleted(currency)) {
|
||||||
// FIXME - KW - if auto waiving interest need to create
|
// FIXME - KW - if auto waiving interest need to create
|
||||||
// another transaction to handle this.
|
// another transaction to handle this.
|
||||||
currentInstallment.waiveInterestComponent(currentInstallment.getInterestCharged(currency));
|
currentInstallment.waiveInterestComponent(transactionDate, currentInstallment.getInterestCharged(currency));
|
||||||
}
|
}
|
||||||
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
@@ -93,16 +93,16 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. pay of principal with over payment.
|
// 1. pay of principal with over payment.
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +117,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) {
|
final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) {
|
||||||
|
|
||||||
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
@@ -125,27 +126,30 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
|||||||
Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
|
|
||||||
if (loanTransaction.isChargesWaiver()) {
|
if (loanTransaction.isChargesWaiver()) {
|
||||||
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(loanTransaction.getPenaltyChargesPortion(currency));
|
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(loanTransaction.getFeeChargesPortion(currency));
|
feeChargesPortion = currentInstallment
|
||||||
|
.waiveFeeChargesComponent(transactionDate, loanTransaction.getFeeChargesPortion(currency));
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
} else if (loanTransaction.isInterestWaiver()) {
|
} else if (loanTransaction.isInterestWaiver()) {
|
||||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
} else {
|
} else {
|
||||||
// 1. pay of principal before interest.
|
// 1. pay of principal before interest.
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
|
||||||
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -1296,17 +1296,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency);
|
regenerateRepaymentSchedule(loanScheduleFactory, currency);
|
||||||
|
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||||
} else {
|
} else {
|
||||||
|
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
* Adding a temporary Interest applied transaction (applying the
|
|
||||||
* full Interest as soon as the loan is disbursed), will have to
|
|
||||||
* re-look into this logic once loans with Interest calculation need
|
|
||||||
* to be created or more advances accrual accounting support has to
|
|
||||||
* be provided
|
|
||||||
***/
|
|
||||||
Money interestApplied = Money.of(this.getCurrency(), this.summary.getTotalInterestCharged());
|
Money interestApplied = Money.of(this.getCurrency(), this.summary.getTotalInterestCharged());
|
||||||
final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(this, interestApplied, actualDisbursementDate);
|
final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(this, interestApplied, actualDisbursementDate);
|
||||||
this.loanTransactions.add(interestAppliedTransaction);
|
this.loanTransactions.add(interestAppliedTransaction);
|
||||||
@@ -1315,6 +1310,13 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) {
|
||||||
|
|
||||||
|
for (LoanRepaymentScheduleInstallment repaymentPeriod : this.repaymentScheduleInstallments) {
|
||||||
|
repaymentPeriod.updateDerivedFields(this.loanCurrency(), actualDisbursementDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ability to regenerate the repayment schedule based on the loans current
|
* Ability to regenerate the repayment schedule based on the loans current
|
||||||
* details/state.
|
* details/state.
|
||||||
@@ -1623,7 +1625,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public LocalDate possibleNextRepaymentDate() {
|
public LocalDate possibleNextRepaymentDate() {
|
||||||
LocalDate earliestUnpaidInstallmentDate = new LocalDate();
|
LocalDate earliestUnpaidInstallmentDate = new LocalDate();
|
||||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
if (installment.isNotFullyCompleted()) {
|
if (installment.isNotFullyPaidOff()) {
|
||||||
earliestUnpaidInstallmentDate = installment.getDueDate();
|
earliestUnpaidInstallmentDate = installment.getDueDate();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1649,7 +1651,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
Money possibleNextRepaymentAmount = Money.zero(currency);
|
Money possibleNextRepaymentAmount = Money.zero(currency);
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
if (installment.isNotFullyCompleted()) {
|
if (installment.isNotFullyPaidOff()) {
|
||||||
possibleNextRepaymentAmount = installment.getTotalOutstanding(currency);
|
possibleNextRepaymentAmount = installment.getTotalOutstanding(currency);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1670,7 +1672,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||||
|
|
||||||
final Money outstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency());
|
final Money outstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency());
|
||||||
if (scheduledRepayment.isOverdueOn(new LocalDate()) && scheduledRepayment.isNotFullyCompleted()
|
if (scheduledRepayment.isOverdueOn(new LocalDate()) && scheduledRepayment.isNotFullyPaidOff()
|
||||||
&& outstandingForPeriod.isGreaterThanZero()) {
|
&& outstandingForPeriod.isGreaterThanZero()) {
|
||||||
transactionDate = scheduledRepayment.getDueDate();
|
transactionDate = scheduledRepayment.getDueDate();
|
||||||
possibleInterestToWaive = outstandingForPeriod;
|
possibleInterestToWaive = outstandingForPeriod;
|
||||||
|
|||||||
+58
-34
@@ -87,13 +87,18 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
private BigDecimal penaltyChargesWaived;
|
private BigDecimal penaltyChargesWaived;
|
||||||
|
|
||||||
@Column(name = "completed_derived", nullable = false)
|
@Column(name = "completed_derived", nullable = false)
|
||||||
private boolean completed;
|
private boolean obligationsMet;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Temporal(TemporalType.DATE)
|
||||||
|
@Column(name = "obligations_met_on_date")
|
||||||
|
private Date obligationsMetOnDate;
|
||||||
|
|
||||||
protected LoanRepaymentScheduleInstallment() {
|
protected LoanRepaymentScheduleInstallment() {
|
||||||
this.installmentNumber = null;
|
this.installmentNumber = null;
|
||||||
this.fromDate = null;
|
this.fromDate = null;
|
||||||
this.dueDate = null;
|
this.dueDate = null;
|
||||||
this.completed = false;
|
this.obligationsMet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
||||||
@@ -107,7 +112,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.interestCharged = defaultToNullIfZero(interest);
|
this.interestCharged = defaultToNullIfZero(interest);
|
||||||
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
|
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
|
||||||
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
|
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
|
||||||
this.completed = false;
|
this.obligationsMet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
||||||
@@ -173,8 +178,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Money getInterestOutstanding(final MonetaryCurrency currency) {
|
public Money getInterestOutstanding(final MonetaryCurrency currency) {
|
||||||
final Money interestAccountedFor = getInterestPaid(currency).plus(getInterestWaived(currency)).plus(
|
final Money interestAccountedFor = getInterestPaid(currency).plus(getInterestWaived(currency))
|
||||||
getInterestWrittenOff(currency));
|
.plus(getInterestWrittenOff(currency));
|
||||||
return getInterestCharged(currency).minus(interestAccountedFor);
|
return getInterestCharged(currency).minus(interestAccountedFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,12 +244,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFullyCompleted() {
|
public boolean isObligationsMet() {
|
||||||
return this.completed;
|
return this.obligationsMet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotFullyCompleted() {
|
public boolean isNotFullyPaidOff() {
|
||||||
return !this.completed;
|
return !this.obligationsMet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {
|
public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {
|
||||||
@@ -268,10 +273,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.penaltyChargesWaived = null;
|
this.penaltyChargesWaived = null;
|
||||||
this.penaltyChargesWrittenOff = null;
|
this.penaltyChargesWrittenOff = null;
|
||||||
|
|
||||||
this.completed = false;
|
this.obligationsMet = false;
|
||||||
|
this.obligationsMetOnDate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money payPenaltyChargesComponent(final Money transactionAmountRemaining) {
|
public Money payPenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
|
|
||||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money penaltyPortionOfTransaction = Money.zero(currency);
|
Money penaltyPortionOfTransaction = Money.zero(currency);
|
||||||
@@ -287,12 +293,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.penaltyChargesPaid = defaultToNullIfZero(this.penaltyChargesPaid);
|
this.penaltyChargesPaid = defaultToNullIfZero(this.penaltyChargesPaid);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return penaltyPortionOfTransaction;
|
return penaltyPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money payFeeChargesComponent(final Money transactionAmountRemaining) {
|
public Money payFeeChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
|
|
||||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money feePortionOfTransaction = Money.zero(currency);
|
Money feePortionOfTransaction = Money.zero(currency);
|
||||||
@@ -308,12 +314,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.feeChargesPaid = defaultToNullIfZero(this.feeChargesPaid);
|
this.feeChargesPaid = defaultToNullIfZero(this.feeChargesPaid);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return feePortionOfTransaction;
|
return feePortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money payInterestComponent(final Money transactionAmountRemaining) {
|
public Money payInterestComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
|
|
||||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money interestPortionOfTransaction = Money.zero(currency);
|
Money interestPortionOfTransaction = Money.zero(currency);
|
||||||
@@ -329,12 +335,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.interestPaid = defaultToNullIfZero(this.interestPaid);
|
this.interestPaid = defaultToNullIfZero(this.interestPaid);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return interestPortionOfTransaction;
|
return interestPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money payPrincipalComponent(final Money transactionAmountRemaining) {
|
public Money payPrincipalComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
|
|
||||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money principalPortionOfTransaction = Money.zero(currency);
|
Money principalPortionOfTransaction = Money.zero(currency);
|
||||||
@@ -350,12 +356,19 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
|
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return principalPortionOfTransaction;
|
return principalPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money waiveInterestComponent(final Money transactionAmountRemaining) {
|
private void checkIfRepaymentPeriodObligationsAreMet(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||||
|
this.obligationsMet = getTotalOutstanding(currency).isZero();
|
||||||
|
if (this.obligationsMet) {
|
||||||
|
this.obligationsMetOnDate = transactionDate.toDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money waiveInterestComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money waivedInterestPortionOfTransaction = Money.zero(currency);
|
Money waivedInterestPortionOfTransaction = Money.zero(currency);
|
||||||
|
|
||||||
@@ -370,12 +383,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.interestWaived = defaultToNullIfZero(this.interestWaived);
|
this.interestWaived = defaultToNullIfZero(this.interestWaived);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return waivedInterestPortionOfTransaction;
|
return waivedInterestPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money waivePenaltyChargesComponent(final Money transactionAmountRemaining) {
|
public Money waivePenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money waivedPenaltyChargesPortionOfTransaction = Money.zero(currency);
|
Money waivedPenaltyChargesPortionOfTransaction = Money.zero(currency);
|
||||||
|
|
||||||
@@ -390,12 +403,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.penaltyChargesWaived = defaultToNullIfZero(this.penaltyChargesWaived);
|
this.penaltyChargesWaived = defaultToNullIfZero(this.penaltyChargesWaived);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return waivedPenaltyChargesPortionOfTransaction;
|
return waivedPenaltyChargesPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money waiveFeeChargesComponent(final Money transactionAmountRemaining) {
|
public Money waiveFeeChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||||
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
|
Money waivedFeeChargesPortionOfTransaction = Money.zero(currency);
|
||||||
|
|
||||||
@@ -410,47 +423,51 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
this.feeChargesWaived = defaultToNullIfZero(this.feeChargesWaived);
|
this.feeChargesWaived = defaultToNullIfZero(this.feeChargesWaived);
|
||||||
|
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return waivedFeeChargesPortionOfTransaction;
|
return waivedFeeChargesPortionOfTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) {
|
public Money writeOffOutstandingPrincipal(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||||
|
|
||||||
final Money principalDue = getPrincipalOutstanding(currency);
|
final Money principalDue = getPrincipalOutstanding(currency);
|
||||||
this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
|
this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
|
||||||
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return principalDue;
|
return principalDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money writeOffOutstandingInterest(final MonetaryCurrency currency) {
|
public Money writeOffOutstandingInterest(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||||
|
|
||||||
final Money interestDue = getInterestOutstanding(currency);
|
final Money interestDue = getInterestOutstanding(currency);
|
||||||
this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
|
this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
|
||||||
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return interestDue;
|
return interestDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money writeOffOutstandingFeeCharges(final MonetaryCurrency currency) {
|
public Money writeOffOutstandingFeeCharges(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||||
final Money feeChargesOutstanding = getFeeChargesOutstanding(currency);
|
final Money feeChargesOutstanding = getFeeChargesOutstanding(currency);
|
||||||
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesOutstanding.getAmount());
|
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesOutstanding.getAmount());
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
|
||||||
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return feeChargesOutstanding;
|
return feeChargesOutstanding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money writeOffOutstandingPenaltyCharges(final MonetaryCurrency currency) {
|
public Money writeOffOutstandingPenaltyCharges(final LocalDate transactionDate, final MonetaryCurrency currency) {
|
||||||
final Money penaltyChargesOutstanding = getPenaltyChargesOutstanding(currency);
|
final Money penaltyChargesOutstanding = getPenaltyChargesOutstanding(currency);
|
||||||
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesOutstanding.getAmount());
|
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesOutstanding.getAmount());
|
||||||
this.completed = getTotalOutstanding(currency).isZero();
|
|
||||||
|
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
|
||||||
|
|
||||||
return penaltyChargesOutstanding;
|
return penaltyChargesOutstanding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOverdueOn(final LocalDate transactionDate) {
|
public boolean isOverdueOn(final LocalDate date) {
|
||||||
return this.getDueDate().isBefore(transactionDate);
|
return this.getDueDate().isBefore(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateChargePortion(final Money feeChargesDue, final Money feeChargesWaived, final Money feeChargesWrittenOff,
|
public void updateChargePortion(final Money feeChargesDue, final Money feeChargesWaived, final Money feeChargesWrittenOff,
|
||||||
@@ -462,4 +479,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.penaltyChargesWaived = defaultToNullIfZero(penaltyChargesWaived.getAmount());
|
this.penaltyChargesWaived = defaultToNullIfZero(penaltyChargesWaived.getAmount());
|
||||||
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
|
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateDerivedFields(final MonetaryCurrency currency, final LocalDate actualDisbursementDate) {
|
||||||
|
if (!this.obligationsMet && getTotalOutstanding(currency).isZero()) {
|
||||||
|
this.obligationsMet = true;
|
||||||
|
this.obligationsMetOnDate = actualDisbursementDate.toDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+86
-92
@@ -17,107 +17,101 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
* For ALL types of transactions, pays off components in order of interest, then
|
* For ALL types of transactions, pays off components in order of interest, then
|
||||||
* principal.
|
* principal.
|
||||||
*
|
*
|
||||||
* Other formulas exist on mifos where you can choose 'Declining-Balance Interest Recalculation' which simply
|
* Other formulas exist on mifos where you can choose 'Declining-Balance
|
||||||
* means, recalculate the interest component based on the how much principal is outstanding at a point in time;
|
* Interest Recalculation' which simply means, recalculate the interest
|
||||||
|
* component based on the how much principal is outstanding at a point in time;
|
||||||
* but this isnt trying to model that option only the basic one for now.
|
* but this isnt trying to model that option only the basic one for now.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends
|
public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends AbstractLoanRepaymentScheduleTransactionProcessor {
|
||||||
AbstractLoanRepaymentScheduleTransactionProcessor {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(
|
protected 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) {
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
return transactionDate.isBefore(currentInstallment.getDueDate());
|
return transactionDate.isBefore(currentInstallment.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For early/'in advance' repayments, pay off in the same way as on-time payments,
|
* For early/'in advance' repayments, pay off in the same way as on-time
|
||||||
* interest first then principal.
|
* payments, interest first then principal.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, 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,
|
* For late repayments, pay off in the same way as on-time payments,
|
||||||
transactionAmountUnprocessed);
|
* 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) {
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
// penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(loanTransaction.getPenaltyChargesPortion(currency));
|
|
||||||
// transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
|
||||||
//
|
|
||||||
// feeChargesPortion = currentInstallment.waiveFeeChargesComponent(loanTransaction.getFeeChargesPortion(currency));
|
|
||||||
// transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
|
||||||
//
|
|
||||||
// zero this type of transaction and ignore it for now.
|
|
||||||
transactionAmountRemaining = Money.zero(currency);
|
|
||||||
} else if (loanTransaction.isInterestWaiver()) {
|
|
||||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
|
||||||
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
} 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);
|
|
||||||
|
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction,
|
* For normal on-time repayments, pays off interest first, then principal.
|
||||||
final Money loanOverPaymentAmount) {
|
*/
|
||||||
// TODO - KW - dont do anything with loan over-payment for now
|
@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()) {
|
||||||
|
// penaltyChargesPortion =
|
||||||
|
// currentInstallment.waivePenaltyChargesComponent(loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
// transactionAmountRemaining =
|
||||||
|
// transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
//
|
||||||
|
// feeChargesPortion =
|
||||||
|
// currentInstallment.waiveFeeChargesComponent(loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
// transactionAmountRemaining =
|
||||||
|
// transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
//
|
||||||
|
// 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 {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
|
// TODO - KW - dont do anything with loan over-payment for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+153
-155
@@ -16,171 +16,169 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
*
|
*
|
||||||
* From https://mifosforge.jira.com/browse/MIFOS-5636:
|
* From https://mifosforge.jira.com/browse/MIFOS-5636:
|
||||||
*
|
*
|
||||||
* Per RBI regulations, all interest must be paid (both current and overdue) before principal is paid.
|
* Per RBI regulations, all interest must be paid (both current and overdue)
|
||||||
|
* before principal is paid.
|
||||||
*
|
*
|
||||||
* For example on a loan with two installments due (one current and one overdue) of 220 each (200 principal + 20 interest):
|
* For example on a loan with two installments due (one current and one overdue)
|
||||||
|
* of 220 each (200 principal + 20 interest):
|
||||||
*
|
*
|
||||||
* Partial Payment of 40
|
* Partial Payment of 40 20 Payment to interest on Installment #1 (200 principal
|
||||||
* 20 Payment to interest on Installment #1 (200 principal remaining)
|
* remaining) 20 Payment to interest on Installment #2 (200 principal remaining)
|
||||||
* 20 Payment to interest on Installment #2 (200 principal remaining)
|
|
||||||
*/
|
*/
|
||||||
public class RBILoanRepaymentScheduleTransactionProcessor 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
|
||||||
* date
|
* date
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTransactionInAdvanceOfInstallment(
|
protected 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) {
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
|
||||||
|
|
||||||
return transactionDate.isBefore(currentInstallment.getDueDate());
|
return transactionDate.isBefore(currentInstallment.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For early/'in advance' repayments, pays off principal component only.
|
* For early/'in advance' repayments, pays off principal component only.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(
|
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments,
|
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||||
final LoanTransaction loanTransaction,
|
|
||||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
|
||||||
|
|
||||||
return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, 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) {
|
|
||||||
|
|
||||||
// pay of overdue and current interest due given transaction date
|
|
||||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
|
||||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
|
||||||
Money interestWaivedPortion = Money.zero(currency);
|
|
||||||
Money feeChargesPortion = Money.zero(currency);
|
|
||||||
Money penaltyChargesPortion = Money.zero(currency);
|
|
||||||
|
|
||||||
if (loanTransaction.isInterestWaiver()) {
|
|
||||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
|
||||||
|
|
||||||
final Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestWaivedPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment(loanTransaction.getTransactionDate(), installments);
|
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
Money principalPortion = Money.zero(currency);
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, Money.zero(currency), Money.zero(currency));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With whatever is remaining, pay off principal components of installments
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
|
||||||
if (installment.isPrincipalNotCompleted(currency) && transactionAmountRemaining.isGreaterThanZero()) {
|
|
||||||
Money principalPortion = installment.payPrincipalComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
|
|
||||||
Money interestPortion = Money.zero(currency);
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, Money.zero(currency), Money.zero(currency));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoanRepaymentScheduleInstallment nearestInstallment(final LocalDate transactionDate, List<LoanRepaymentScheduleInstallment> installments) {
|
/**
|
||||||
|
* For late repayments, pay off in the same way as on-time payments,
|
||||||
LoanRepaymentScheduleInstallment nearest = installments.get(0);
|
* interest first then principal.
|
||||||
|
*/
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
@Override
|
||||||
if (installment.getDueDate().isAfter(transactionDate) || installment.getDueDate().isEqual(transactionDate)) {
|
protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
nearest = installment;
|
final List<LoanRepaymentScheduleInstallment> installments, final LoanTransaction loanTransaction,
|
||||||
break;
|
final Money transactionAmountUnprocessed) {
|
||||||
}
|
|
||||||
}
|
|
||||||
return nearest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// pay of overdue and current interest due given transaction date
|
||||||
* For normal on-time repayments, pays off interest first, then principal.
|
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
*/
|
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||||
@Override
|
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||||
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(
|
Money interestWaivedPortion = Money.zero(currency);
|
||||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
Money feeChargesPortion = Money.zero(currency);
|
||||||
final LoanTransaction loanTransaction,
|
Money penaltyChargesPortion = Money.zero(currency);
|
||||||
final Money transactionAmountUnprocessed) {
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
|
|
||||||
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(loanTransaction.getPenaltyChargesPortion(currency));
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
|
||||||
|
|
||||||
feeChargesPortion = currentInstallment.waiveFeeChargesComponent(loanTransaction.getFeeChargesPortion(currency));
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
|
||||||
|
|
||||||
} else if (loanTransaction.isInterestWaiver()) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
|
||||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
|
||||||
return transactionAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
if (loanTransaction.isInterestWaiver()) {
|
||||||
@Override
|
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction,
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||||
final Money loanOverPaymentAmount) {
|
|
||||||
// dont do anything for with loan over-payment
|
final Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||||
}
|
loanTransaction.updateComponents(principalPortion, interestWaivedPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment(
|
||||||
|
loanTransaction.getTransactionDate(), installments);
|
||||||
|
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
if (installment.isInterestDue(currency)
|
||||||
|
&& (installment.isOverdueOn(loanTransaction.getTransactionDate()) || installment.getInstallmentNumber().equals(
|
||||||
|
currentInstallmentBasedOnTransactionDate.getInstallmentNumber()))) {
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
Money interestPortion = installment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
Money principalPortion = Money.zero(currency);
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, Money.zero(currency), Money.zero(currency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With whatever is remaining, pay off principal components of
|
||||||
|
// installments
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
if (installment.isPrincipalNotCompleted(currency) && transactionAmountRemaining.isGreaterThanZero()) {
|
||||||
|
Money principalPortion = installment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
|
||||||
|
Money interestPortion = Money.zero(currency);
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, Money.zero(currency), Money.zero(currency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanRepaymentScheduleInstallment nearestInstallment(final LocalDate transactionDate,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
|
|
||||||
|
LoanRepaymentScheduleInstallment nearest = installments.get(0);
|
||||||
|
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
if (installment.getDueDate().isAfter(transactionDate) || installment.getDueDate().isEqual(transactionDate)) {
|
||||||
|
nearest = installment;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
||||||
|
|
||||||
|
penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate,
|
||||||
|
loanTransaction.getPenaltyChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment
|
||||||
|
.waiveFeeChargesComponent(transactionDate, loanTransaction.getFeeChargesPortion(currency));
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
} else if (loanTransaction.isInterestWaiver()) {
|
||||||
|
interestPortion = currentInstallment.waiveInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||||
|
|
||||||
|
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||||
|
|
||||||
|
interestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||||
|
|
||||||
|
principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
|
||||||
|
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||||
|
}
|
||||||
|
|
||||||
|
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||||
|
return transactionAmountRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Override
|
||||||
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
|
// dont do anything for with loan over-payment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+33
-17
@@ -19,6 +19,10 @@ public class LoanSchedulePeriodData {
|
|||||||
private final Integer period;
|
private final Integer period;
|
||||||
private final LocalDate fromDate;
|
private final LocalDate fromDate;
|
||||||
private final LocalDate dueDate;
|
private final LocalDate dueDate;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final LocalDate obligationsMetOnDate;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final Boolean complete;
|
||||||
private final Integer daysInPeriod;
|
private final Integer daysInPeriod;
|
||||||
private final BigDecimal principalDisbursed;
|
private final BigDecimal principalDisbursed;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -79,8 +83,9 @@ public class LoanSchedulePeriodData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static LoanSchedulePeriodData repaymentPeriodWithPayments(@SuppressWarnings("unused") final Long loanId,
|
public static LoanSchedulePeriodData repaymentPeriodWithPayments(@SuppressWarnings("unused") final Long loanId,
|
||||||
final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalOriginalDue,
|
final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final LocalDate obligationsMetOnDate,
|
||||||
final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
|
final boolean complete, final BigDecimal principalOriginalDue, final BigDecimal principalPaid,
|
||||||
|
final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
|
||||||
final BigDecimal outstandingPrincipalBalanceOfLoan, final BigDecimal interestDueOnPrincipalOutstanding,
|
final BigDecimal outstandingPrincipalBalanceOfLoan, final BigDecimal interestDueOnPrincipalOutstanding,
|
||||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||||
final BigDecimal interestOutstanding, final BigDecimal feeChargesDue, final BigDecimal feeChargesPaid,
|
final BigDecimal interestOutstanding, final BigDecimal feeChargesDue, final BigDecimal feeChargesPaid,
|
||||||
@@ -90,12 +95,12 @@ public class LoanSchedulePeriodData {
|
|||||||
final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
||||||
final BigDecimal totalActualCostOfLoanForPeriod) {
|
final BigDecimal totalActualCostOfLoanForPeriod) {
|
||||||
|
|
||||||
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalOriginalDue, principalPaid, principalWrittenOff,
|
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, obligationsMetOnDate, complete, principalOriginalDue,
|
||||||
principalOutstanding, outstandingPrincipalBalanceOfLoan, interestDueOnPrincipalOutstanding, interestPaid, interestWaived,
|
principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
||||||
interestWrittenOff, interestOutstanding, feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff,
|
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesDue,
|
||||||
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff,
|
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
|
||||||
penaltyChargesOutstanding, totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding,
|
penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaid, totalWaived,
|
||||||
totalActualCostOfLoanForPeriod);
|
totalWrittenOff, totalOutstanding, totalActualCostOfLoanForPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -107,6 +112,8 @@ public class LoanSchedulePeriodData {
|
|||||||
this.period = periodNumber;
|
this.period = periodNumber;
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
|
this.obligationsMetOnDate = null;
|
||||||
|
this.complete = null;
|
||||||
if (fromDate != null) {
|
if (fromDate != null) {
|
||||||
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
||||||
} else {
|
} else {
|
||||||
@@ -160,6 +167,10 @@ public class LoanSchedulePeriodData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* used for repayment only period when creating an empty loan schedule for
|
||||||
|
* preview etc
|
||||||
|
*/
|
||||||
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
||||||
final BigDecimal principalOriginalDue, final BigDecimal principalOutstanding,
|
final BigDecimal principalOriginalDue, final BigDecimal principalOutstanding,
|
||||||
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal feeChargesDueForPeriod,
|
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal feeChargesDueForPeriod,
|
||||||
@@ -167,6 +178,8 @@ public class LoanSchedulePeriodData {
|
|||||||
this.period = periodNumber;
|
this.period = periodNumber;
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
|
this.obligationsMetOnDate = null;
|
||||||
|
this.complete = null;
|
||||||
if (fromDate != null) {
|
if (fromDate != null) {
|
||||||
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
||||||
} else {
|
} else {
|
||||||
@@ -219,18 +232,21 @@ public class LoanSchedulePeriodData {
|
|||||||
* principal, interest & charges along with what portion of each is paid.
|
* principal, interest & charges along with what portion of each is paid.
|
||||||
*/
|
*/
|
||||||
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
||||||
final BigDecimal principalOriginalDue, final BigDecimal principalPaid, final BigDecimal principalWrittenOff,
|
final LocalDate obligationsMetOnDate, final boolean complete, final BigDecimal principalOriginalDue,
|
||||||
final BigDecimal principalOutstanding, final BigDecimal principalLoanBalanceOutstanding,
|
final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
|
||||||
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal interestPaid, final BigDecimal interestWaived,
|
final BigDecimal principalLoanBalanceOutstanding, final BigDecimal interestDueOnPrincipalOutstanding,
|
||||||
final BigDecimal interestWrittenOff, final BigDecimal interestOutstanding, final BigDecimal feeChargesDue,
|
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||||
final BigDecimal feeChargesPaid, final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff,
|
final BigDecimal interestOutstanding, final BigDecimal feeChargesDue, final BigDecimal feeChargesPaid,
|
||||||
final BigDecimal feeChargesOutstanding, final BigDecimal penaltyChargesDue, final BigDecimal penaltyChargesPaid,
|
final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesOutstanding,
|
||||||
final BigDecimal penaltyChargesWaived, final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding,
|
final BigDecimal penaltyChargesDue, final BigDecimal penaltyChargesPaid, final BigDecimal penaltyChargesWaived,
|
||||||
final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff,
|
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding, final BigDecimal totalDueForPeriod,
|
||||||
final BigDecimal totalOutstanding, final BigDecimal totalActualCostOfLoanForPeriod) {
|
final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
|
||||||
|
final BigDecimal totalActualCostOfLoanForPeriod) {
|
||||||
this.period = periodNumber;
|
this.period = periodNumber;
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
|
this.obligationsMetOnDate = obligationsMetOnDate;
|
||||||
|
this.complete = complete;
|
||||||
if (fromDate != null) {
|
if (fromDate != null) {
|
||||||
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+27
-4
@@ -94,10 +94,33 @@ public final class LoanEventApiJsonValidator {
|
|||||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void validateNewRepaymentTransaction(final String json) {
|
||||||
* @param baseDataValidator
|
|
||||||
* @param element
|
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||||
*/
|
|
||||||
|
final Set<String> transactionParameters = new HashSet<String>(Arrays.asList("transactionDate", "transactionAmount", "note",
|
||||||
|
"locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
|
||||||
|
|
||||||
|
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||||
|
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
|
||||||
|
|
||||||
|
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||||
|
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
|
||||||
|
|
||||||
|
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||||
|
final LocalDate transactionDate = fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
|
||||||
|
baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
|
||||||
|
|
||||||
|
final BigDecimal transactionAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
|
||||||
|
baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount();
|
||||||
|
|
||||||
|
final String note = fromApiJsonHelper.extractStringNamed("note", element);
|
||||||
|
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
|
||||||
|
|
||||||
|
validatePaymentDetails(baseDataValidator, element);
|
||||||
|
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||||
|
}
|
||||||
|
|
||||||
private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
|
||||||
// Validate all string payment detail fields for max length
|
// Validate all string payment detail fields for max length
|
||||||
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed("paymentTypeId", element);
|
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed("paymentTypeId", element);
|
||||||
|
|||||||
+9
-7
@@ -672,7 +672,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
public String schema() {
|
public String schema() {
|
||||||
|
|
||||||
return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, "
|
return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, ls.obligations_met_on_date as obligationsMetOnDate, ls.completed_derived as complete,"
|
||||||
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, "
|
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, "
|
||||||
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, "
|
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, "
|
||||||
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
|
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
|
||||||
@@ -716,6 +716,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final Integer period = JdbcSupport.getInteger(rs, "period");
|
final Integer period = JdbcSupport.getInteger(rs, "period");
|
||||||
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
|
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
|
||||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||||
|
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
|
||||||
|
final boolean complete = rs.getBoolean("complete");
|
||||||
|
|
||||||
Integer daysInPeriod = Integer.valueOf(0);
|
Integer daysInPeriod = Integer.valueOf(0);
|
||||||
if (fromDate != null) {
|
if (fromDate != null) {
|
||||||
@@ -788,12 +790,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
|
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
|
||||||
|
|
||||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate,
|
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate,
|
||||||
dueDate, principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
dueDate, obligationsMetOnDate, complete, principalDue, principalPaid, principalWrittenOff, principalOutstanding,
|
||||||
interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue,
|
outstandingPrincipalBalanceOfLoan, interestExpectedDue, interestPaid, interestWaived, interestWrittenOff,
|
||||||
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue,
|
interestOutstanding, feeChargesExpectedDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff,
|
||||||
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
|
feeChargesOutstanding, penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived,
|
||||||
totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod,
|
penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod,
|
||||||
totalActualCostOfLoanForPeriod);
|
totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod);
|
||||||
|
|
||||||
periods.add(periodData);
|
periods.add(periodData);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -297,7 +297,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
context.authenticatedUser();
|
context.authenticatedUser();
|
||||||
|
|
||||||
this.loanEventApiJsonValidator.validateTransaction(command.json());
|
this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json());
|
||||||
|
|
||||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
|||||||
+48
-46
@@ -8,54 +8,56 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInsta
|
|||||||
|
|
||||||
public class LoanRepaymentScheduleInstallmentBuilder {
|
public class LoanRepaymentScheduleInstallmentBuilder {
|
||||||
|
|
||||||
private Loan loan = null;
|
private final Loan loan = null;
|
||||||
private Integer installmentNumber = Integer.valueOf(1);
|
private Integer installmentNumber = Integer.valueOf(1);
|
||||||
private LocalDate fromDate = LocalDate.now();
|
private final LocalDate fromDate = LocalDate.now();
|
||||||
private LocalDate dueDate = LocalDate.now();
|
private LocalDate dueDate = LocalDate.now();
|
||||||
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
|
private final LocalDate latestTransactionDate = LocalDate.now();
|
||||||
private Money principal = new MoneyBuilder().build();
|
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
|
||||||
private Money interest = new MoneyBuilder().build();
|
private Money principal = new MoneyBuilder().build();
|
||||||
private Money feeCharges = new MoneyBuilder().build();
|
private Money interest = new MoneyBuilder().build();
|
||||||
private Money penaltyCharges = new MoneyBuilder().build();
|
private final Money feeCharges = new MoneyBuilder().build();
|
||||||
private boolean completed = false;
|
private final Money penaltyCharges = new MoneyBuilder().build();
|
||||||
|
private boolean completed = false;
|
||||||
public LoanRepaymentScheduleInstallmentBuilder(MonetaryCurrency currencyDetail) {
|
|
||||||
this.currencyDetail = currencyDetail;
|
|
||||||
this.principal = new MoneyBuilder().with(currencyDetail).build();
|
|
||||||
this.interest = new MoneyBuilder().with(currencyDetail).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment build() {
|
public LoanRepaymentScheduleInstallmentBuilder(final MonetaryCurrency currencyDetail) {
|
||||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, fromDate, dueDate, principal.getAmount(), interest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount());
|
this.currencyDetail = currencyDetail;
|
||||||
if (completed) {
|
this.principal = new MoneyBuilder().with(currencyDetail).build();
|
||||||
installment.payPrincipalComponent(principal);
|
this.interest = new MoneyBuilder().with(currencyDetail).build();
|
||||||
installment.payInterestComponent(interest);
|
}
|
||||||
}
|
|
||||||
return installment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallmentBuilder withPrincipal(String withPrincipal) {
|
public LoanRepaymentScheduleInstallment build() {
|
||||||
this.principal = new MoneyBuilder().with(currencyDetail).with(withPrincipal).build();
|
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, fromDate, dueDate,
|
||||||
return this;
|
principal.getAmount(), interest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount());
|
||||||
}
|
if (completed) {
|
||||||
|
installment.payPrincipalComponent(latestTransactionDate, principal);
|
||||||
public LoanRepaymentScheduleInstallmentBuilder withInterest(String withInterest) {
|
installment.payInterestComponent(latestTransactionDate, interest);
|
||||||
this.interest = new MoneyBuilder().with(currencyDetail).with(withInterest).build();
|
}
|
||||||
return this;
|
return installment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallmentBuilder withDueDate(LocalDate withDueDate) {
|
public LoanRepaymentScheduleInstallmentBuilder withPrincipal(final String withPrincipal) {
|
||||||
this.dueDate = withDueDate;
|
this.principal = new MoneyBuilder().with(currencyDetail).with(withPrincipal).build();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallmentBuilder completed() {
|
public LoanRepaymentScheduleInstallmentBuilder withInterest(final String withInterest) {
|
||||||
this.completed = true;
|
this.interest = new MoneyBuilder().with(currencyDetail).with(withInterest).build();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallmentBuilder withInstallmentNumber(final int withInstallmentNumber) {
|
public LoanRepaymentScheduleInstallmentBuilder withDueDate(final LocalDate withDueDate) {
|
||||||
this.installmentNumber = withInstallmentNumber;
|
this.dueDate = withDueDate;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public LoanRepaymentScheduleInstallmentBuilder completed() {
|
||||||
|
this.completed = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanRepaymentScheduleInstallmentBuilder withInstallmentNumber(final int withInstallmentNumber) {
|
||||||
|
this.installmentNumber = withInstallmentNumber;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user