mifosx-58: final piece of aligning loan schedule area in prep for integration with charges. Loan schedule presented during loan creation and modify flows are same as one viewed when click loan link on client view.

This commit is contained in:
Keith Woodlock
2012-09-19 17:13:26 +01:00
parent 08fc9ff4d6
commit a6a74a401b
12 changed files with 458 additions and 246 deletions
@@ -28,6 +28,7 @@ import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
import org.mifosng.platform.api.commands.LoanTransactionCommand;
import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
import org.mifosng.platform.api.data.ChargeData;
import org.mifosng.platform.api.data.DisbursementData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.FundData;
@@ -37,7 +38,6 @@ import org.mifosng.platform.api.data.LoanChargeData;
import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanProductLookup;
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanScheduleData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.api.data.StaffData;
@@ -180,7 +180,7 @@ public class LoansApiResource {
int loanRepaymentsCount = 0;
Collection<LoanRepaymentTransactionData> loanRepayments = null;
LoanScheduleData repaymentSchedule = null;
NewLoanScheduleData repaymentSchedule = null;
LoanPermissionData permissions = null;
Collection<LoanChargeData> charges = null;
@@ -198,7 +198,8 @@ public class LoansApiResource {
loanRepayments = currentLoanRepayments;
loanRepaymentsCount = loanRepayments.size();
}
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(), loanRepayments);
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(), singleDisbursement);
MoneyData tolerance = MoneyData.of(loanBasicDetails.getCurrency(), loanBasicDetails.getInArrearsTolerance());
MoneyData totalOutstandingMoney = MoneyData.of(loanBasicDetails.getCurrency(), repaymentSchedule.totalOutstanding());
@@ -28,12 +28,16 @@ public class NewLoanScheduleData {
* The cumulative total of all money (principal) disbursed to the loan applicant.
*/
private final BigDecimal cumulativePrincipalDisbursed;
/**
* The cumulative total of all <code>principalDue</code> for each period of loan schedule. (originalPrincipal)
*/
private final BigDecimal cumulativePrincipalDue;
private final BigDecimal cumulativePrincipalPaid;
private final BigDecimal cumulativePrincipalOutstanding;
/**
* The total interest expected on any principal disbursed.
* The cumulative total of interest expected on any principal disbursed. (originalInterest)
*/
private final BigDecimal cumulativeInterestExpected;
private final BigDecimal cumulativeInterestPaid;
@@ -58,14 +62,35 @@ public class NewLoanScheduleData {
private final BigDecimal totalCostOfLoan;
/**
* The sum of <code>principalDisbursed</code> and <code>totalCostOfLoan</code>.
* The sum of <code>principalDisbursed</code> and <code>totalCostOfLoan</code>. (originalTotal)
*/
private final BigDecimal totalExpectedRepayment;
/**
* The cumulative sum of all repayments to date. (totalPaid)
*/
private final BigDecimal totalPaidToDate;
/**
* The cumulative sum of all waivers to date.
*/
private final BigDecimal totalWaivedToDate;
/**
* The cumulative sum of all principal, interest & charges outstanding.
*/
private final BigDecimal totalOutstanding;
/**
* The amount by which this loan is is arrears.
*
* Different to outstanding as arrears is based on whether a portion of the total outstanding is late and above any tolerance setting.
*/
private final BigDecimal totalInArrears;
/**
* <code>periods</code> is collection of data objects containing specific
* information to each period of the loan schedule including disbursment and
* information to each period of the loan schedule including disbursement and
* repayment information.
*/
private final Collection<LoanSchedulePeriodData> periods;
@@ -88,6 +113,9 @@ public class NewLoanScheduleData {
this.totalCostOfLoan = null;
this.totalExpectedRepayment = null;
this.totalPaidToDate = null;
this.totalWaivedToDate = null;
this.totalOutstanding = null;
this.totalInArrears = null;
this.currency = null;
@@ -132,10 +160,62 @@ public class NewLoanScheduleData {
}
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalInArrears = null;
this.totalPaidToDate = BigDecimal.ZERO;
this.totalWaivedToDate = BigDecimal.ZERO;
this.totalOutstanding = totalExpectedRepayment;
this.totalInArrears = BigDecimal.ZERO;
}
/*
* Used when fully populating
*/
public NewLoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods,
final Integer loanTermInDays,
final BigDecimal cumulativePrincipalDisbursed,
final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalPaid,
final BigDecimal cumulativePrincipalOutstanding,
final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeInterestPaid,
final BigDecimal cumulativeInterestWaived,
final BigDecimal cumulativeInterestOutstanding,
final BigDecimal cumulativeChargesToDate,
final BigDecimal cumulativeChargesPaid,
final BigDecimal cumulativeChargesOutstanding,
final BigDecimal totalCostOfLoan,
final BigDecimal totalExpectedRepayment,
final BigDecimal totalPaidToDate,
final BigDecimal totalWaivedToDate,
final BigDecimal totalOutstanding,
final BigDecimal totalInArrears) {
this.currency = currency;
this.periods = periods;
this.loanTermInDays = loanTermInDays;
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
this.cumulativePrincipalDue = cumulativePrincipalDue;
this.cumulativePrincipalPaid = cumulativePrincipalPaid;
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
this.cumulativeInterestExpected = cumulativeInterestExpected;
this.cumulativeInterestPaid = cumulativeInterestPaid;
this.cumulativeInterestWaived = cumulativeInterestWaived;
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
this.cumulativeChargesToDate = cumulativeChargesToDate;
this.cumulativeChargesPaid = cumulativeChargesPaid;
this.cumulativeChargesOutstanding = cumulativeChargesOutstanding;
this.totalCostOfLoan = totalCostOfLoan;
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = totalPaidToDate;
this.totalWaivedToDate = totalWaivedToDate;
this.totalOutstanding = totalOutstanding;
this.totalInArrears = totalInArrears;
}
public Collection<LoanSchedulePeriodData> getPeriods() {
return periods;
}
public BigDecimal totalOutstanding() {
return this.totalOutstanding;
}
}
@@ -0,0 +1,33 @@
package org.mifosng.platform.api.data;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
/**
* Immutable data object representing disbursement information.
*/
public class DisbursementData {
private final LocalDate expectedDisbursementDate;
private final LocalDate actualDisbursementDate;
private final BigDecimal principalDisbursed;
public DisbursementData(final LocalDate expectedDisbursementDate, final LocalDate actualDisbursementDate, final BigDecimal principalDisbursed) {
this.expectedDisbursementDate = expectedDisbursementDate;
this.actualDisbursementDate = actualDisbursementDate;
this.principalDisbursed = principalDisbursed;
}
public LocalDate disbursementDate() {
LocalDate disbursementDate = this.expectedDisbursementDate;
if (actualDisbursementDate != null) {
disbursementDate = actualDisbursementDate;
}
return disbursementDate;
}
public BigDecimal amount() {
return this.principalDisbursed;
}
}
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.NewLoanScheduleData;
/**
* Immutable data object representing loan account data.
@@ -52,7 +53,7 @@ public class LoanAccountData {
private final LocalDate lifeCycleStatusDate;
// associations
private final LoanScheduleData repaymentSchedule;
private final NewLoanScheduleData repaymentSchedule;
private final Collection<LoanRepaymentTransactionData> loanRepayments;
private final Collection<LoanChargeData> charges;
private final LoanPermissionData permissions;
@@ -75,7 +76,7 @@ public class LoanAccountData {
public LoanAccountData(
final LoanBasicDetailsData basicDetails,
final boolean convenienceDataRequired,
final LoanScheduleData repaymentSchedule,
final NewLoanScheduleData repaymentSchedule,
final Collection<LoanRepaymentTransactionData> loanRepayments,
final LoanPermissionData permissions,
final Collection<LoanChargeData> charges,
@@ -10,6 +10,7 @@ import java.math.BigDecimal;
* Note: getter/setter not added on purpose - google-gson only requires fields to generate json.
*/
@SuppressWarnings("unused")
@Deprecated
public class LoanAccountSummaryData {
private final CurrencyData currency;
@@ -508,4 +508,8 @@ public class LoanBasicDetailsData {
public String getLoanOfficerName() {
return loanOfficerName;
}
public DisbursementData toDisburementData() {
return new DisbursementData(this.expectedDisbursementDate, this.actualDisbursementDate, this.principal);
}
}
@@ -1,97 +0,0 @@
package org.mifosng.platform.api.data;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
/**
* Immutable data object representing a repayment period of a loan schedule.
*/
public class LoanRepaymentPeriodData {
@SuppressWarnings("unused")
private final Long loanId;
@SuppressWarnings("unused")
private final Integer period;
private final LocalDate date;
private final BigDecimal principal;
private final BigDecimal principalPaid;
private final BigDecimal principalOutstanding;
private final BigDecimal interest;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
private final BigDecimal interestOutstanding;
private final BigDecimal total;
private final BigDecimal totalPaid;
private final BigDecimal totalWaived;
private final BigDecimal totalOutstanding;
public LoanRepaymentPeriodData(final Long loanId, final Integer period,
final LocalDate date, final BigDecimal principal, final BigDecimal principalPaid,
final BigDecimal principalOutstanding, final BigDecimal interest,
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestOutstanding,
final BigDecimal total, final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalOutstanding) {
this.loanId = loanId;
this.period = period;
this.date = date;
this.principal = principal;
this.principalPaid = principalPaid;
this.principalOutstanding = principalOutstanding;
this.interest = interest;
this.interestPaid = interestPaid;
this.interestWaived = interestWaived;
this.interestOutstanding = interestOutstanding;
this.total = total;
this.totalPaid = totalPaid;
this.totalWaived = totalWaived;
this.totalOutstanding = totalOutstanding;
}
public LocalDate getDate() {
return date;
}
public BigDecimal getPrincipal() {
return principal;
}
public BigDecimal getPrincipalPaid() {
return principalPaid;
}
public BigDecimal getPrincipalOutstanding() {
return principalOutstanding;
}
public BigDecimal getInterest() {
return interest;
}
public BigDecimal getInterestPaid() {
return interestPaid;
}
public BigDecimal getInterestWaived() {
return interestWaived;
}
public BigDecimal getInterestOutstanding() {
return interestOutstanding;
}
public BigDecimal getTotal() {
return total;
}
public BigDecimal getTotalPaid() {
return totalPaid;
}
public BigDecimal getTotalWaived() {
return totalWaived;
}
public BigDecimal getTotalOutstanding() {
return totalOutstanding;
}
}
@@ -1,94 +0,0 @@
package org.mifosng.platform.api.data;
import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
/**
* Immutable data object representing the expected disbursement and repayment schedule for the loan.
*/
public class LoanScheduleData {
private final LoanAccountSummaryData summary;
@SuppressWarnings("unused")
private final CurrencyData currency;
@SuppressWarnings("unused")
private final Collection<LoanRepaymentPeriodData> repaymentPeriods;
public LoanScheduleData(final CurrencyData currency, final Collection<LoanRepaymentPeriodData> repaymentPeriods, final Collection<LoanRepaymentTransactionData> loanRepayments) {
this.summary = derivedSummaryFrom(currency, repaymentPeriods, loanRepayments);
this.currency = currency;
this.repaymentPeriods = repaymentPeriods;
}
private LoanAccountSummaryData derivedSummaryFrom(
final CurrencyData currency,
final Collection<LoanRepaymentPeriodData> repaymentSchedule,
final Collection<LoanRepaymentTransactionData> loanRepayments) {
BigDecimal originalPrincipal = BigDecimal.ZERO;
BigDecimal principalPaid = BigDecimal.ZERO;
BigDecimal principalOutstanding = BigDecimal.ZERO;
BigDecimal originalInterest = BigDecimal.ZERO;
BigDecimal interestPaid = BigDecimal.ZERO;
BigDecimal interestWaived = BigDecimal.ZERO;
BigDecimal interestOutstanding = BigDecimal.ZERO;
BigDecimal originalTotal = BigDecimal.ZERO;
BigDecimal totalPaid = BigDecimal.ZERO;
BigDecimal totalWaived = BigDecimal.ZERO;
BigDecimal totalOutstanding = BigDecimal.ZERO;
BigDecimal totalInArrears = BigDecimal.ZERO;
for (LoanRepaymentPeriodData installment : repaymentSchedule) {
originalPrincipal = originalPrincipal.add(installment.getPrincipal());
principalPaid = principalPaid.add(installment.getPrincipalPaid());
principalOutstanding = principalOutstanding.add(installment.getPrincipalOutstanding());
originalInterest = originalInterest.add(installment.getInterest());
interestPaid = interestPaid.add(installment.getInterestPaid());
interestWaived = interestWaived.add(installment.getInterestWaived());
interestOutstanding = interestOutstanding.add(installment.getInterestOutstanding());
originalTotal = originalTotal.add(installment.getTotal());
totalPaid = totalPaid.add(installment.getTotalPaid());
totalWaived = totalWaived.add(installment.getTotalWaived());
totalOutstanding = totalOutstanding.add(installment.getTotalOutstanding());
if (installment.getDate().isBefore(new LocalDate())) {
totalInArrears = totalInArrears.add(installment.getTotalOutstanding());
}
}
// sum up all repayment transactions
Long waiverType = (long) 4;
MoneyData totalRepaymentAmount = MoneyData.of(currency,BigDecimal.ZERO);
if (loanRepayments != null) {
for (LoanRepaymentTransactionData loanRepayment : loanRepayments) {
Long transactionType = loanRepayment.getTransactionType().getId();
if (transactionType.equals(waiverType)) {
// skip
} else {
totalRepaymentAmount = totalRepaymentAmount.plus(loanRepayment.getTotal());
}
}
}
// detect if loan is in overpaid state
if (totalRepaymentAmount.isGreaterThan(MoneyData.of(currency, totalPaid))) {
BigDecimal difference = totalRepaymentAmount.getAmount().subtract(totalPaid);
totalOutstanding = totalOutstanding.subtract(difference);
}
return new LoanAccountSummaryData(currency, originalPrincipal,
principalPaid, principalOutstanding, originalInterest,
interestPaid, interestWaived, interestOutstanding,
originalTotal, totalPaid, totalWaived, totalOutstanding,
totalInArrears);
}
public BigDecimal totalOutstanding() {
return this.summary.totalOutstanding();
}
}
@@ -21,18 +21,23 @@ public class LoanSchedulePeriodData {
private final BigDecimal principalDue;
private final BigDecimal principalPaid;
private final BigDecimal principalOutstanding;
private final BigDecimal principalTotalBalanceOutstanding;
private final BigDecimal principalLoanBalanceOutstanding;
private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
private final BigDecimal interestOutstanding;
private final BigDecimal chargesDue;
private final BigDecimal chargesPaid;
private final BigDecimal chargesOutstanding;
private final BigDecimal totalOriginalDueForPeriod;
private final BigDecimal totalDueForPeriod;
private final BigDecimal totalPaidForPeriod;
private final BigDecimal totalWaivedForPeriod;
private final BigDecimal totalOutstandingForPeriod;
public static LoanSchedulePeriodData disbursement(final LocalDate disbursementDate, final BigDecimal principalDisbursed) {
Integer periodNumber = Integer.valueOf(0);
@@ -48,6 +53,33 @@ public class LoanSchedulePeriodData {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding, interestDueOnPrincipalOutstanding, totalDueForPeriod);
}
public static LoanSchedulePeriodData repaymentPeriodWithPayments(
final Long loanId,
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalPaid,
final BigDecimal principalOutstanding,
final BigDecimal outstandingPrincipalBalanceOfLoan,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid,
final BigDecimal interestWaived,
final BigDecimal interestOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
final BigDecimal totalOutstanding) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
principalOriginalDue, principalPaid, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding,
totalDueForPeriod);
}
/*
* constructor used for creating period on loan schedule that is only a disbursement (typically first period)
*/
private LoanSchedulePeriodData(
final Integer periodNumber,
final LocalDate fromDate,
@@ -66,25 +98,33 @@ public class LoanSchedulePeriodData {
this.principalDue = null;
this.principalPaid = null;
this.principalOutstanding = null;
this.principalTotalBalanceOutstanding = principalDisbursed;
this.principalLoanBalanceOutstanding = principalDisbursed;
this.interestOriginalDue = null;
this.interestDue = null;
this.interestPaid = null;
this.interestWaived = null;
this.interestOutstanding = null;
this.chargesDue = null;
this.chargesDue = BigDecimal.ZERO;
this.chargesPaid = null;
this.chargesOutstanding = null;
this.totalOriginalDueForPeriod = null;
this.totalDueForPeriod = null;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
this.totalOutstandingForPeriod = null;
}
private LoanSchedulePeriodData(
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue, final BigDecimal principalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal totalDueForPeriod) {
final BigDecimal principalOriginalDue,
final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal totalDueForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
@@ -98,18 +138,76 @@ public class LoanSchedulePeriodData {
this.principalDue = principalOriginalDue;
this.principalPaid = null;
this.principalOutstanding = principalOriginalDue;
this.principalTotalBalanceOutstanding = principalOutstanding;
this.principalLoanBalanceOutstanding = principalOutstanding;
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = null;
this.interestWaived = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.chargesDue = null;
this.chargesDue = BigDecimal.ZERO;
this.chargesPaid = null;
this.chargesOutstanding = null;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod;
}
/*
* Used for creating loan schedule periods with full information on expected principal, interest & charges along with what portion of each is paid.
*/
private LoanSchedulePeriodData(
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalPaid,
final BigDecimal principalOutstanding,
final BigDecimal principalLoanBalanceOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal totalDueForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = null;
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = principalPaid;
this.principalOutstanding = principalOutstanding;
this.principalLoanBalanceOutstanding = principalLoanBalanceOutstanding;
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = BigDecimal.ZERO;
this.interestWaived = BigDecimal.ZERO;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.chargesDue = BigDecimal.ZERO;
this.chargesPaid = BigDecimal.ZERO;
this.chargesOutstanding = BigDecimal.ZERO;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = BigDecimal.ZERO;
this.totalOutstandingForPeriod = totalDueForPeriod;
}
private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
BigDecimal value = BigDecimal.ZERO;
if (possibleNullValue != null) {
value = possibleNullValue;
}
return value;
}
public boolean isRepaymentPeriod() {
@@ -127,12 +225,48 @@ public class LoanSchedulePeriodData {
public LocalDate periodDueDate() {
return this.dueDate;
}
public BigDecimal principalDisbursed() {
return defaultToZeroIfNull(this.principalDisbursed);
}
public BigDecimal principalDue() {
return this.principalDue;
return defaultToZeroIfNull(this.principalDue);
}
public BigDecimal principalPaid() {
return defaultToZeroIfNull(this.principalPaid);
}
public BigDecimal principalOutstanding() {
return defaultToZeroIfNull(this.principalOutstanding);
}
public BigDecimal interestDue() {
return this.interestDue;
return defaultToZeroIfNull(this.interestDue);
}
public BigDecimal interestPaid() {
return defaultToZeroIfNull(this.interestPaid);
}
public BigDecimal interestWaived() {
return defaultToZeroIfNull(this.interestWaived);
}
public BigDecimal interestOutstanding() {
return defaultToZeroIfNull(this.interestOutstanding);
}
public BigDecimal chargesDue() {
return defaultToZeroIfNull(this.chargesDue);
}
public BigDecimal chargesPaid() {
return defaultToZeroIfNull(this.chargesPaid);
}
public BigDecimal chargesOutstanding() {
return defaultToZeroIfNull(this.chargesOutstanding);
}
}
@@ -2,18 +2,19 @@ package org.mifosng.platform.loan.service;
import java.util.Collection;
import org.mifosng.platform.api.NewLoanScheduleData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DisbursementData;
import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanScheduleData;
import org.mifosng.platform.api.data.LoanTransactionData;
public interface LoanReadPlatformService {
LoanBasicDetailsData retrieveLoanAccountDetails(Long loanId);
LoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currencyData, Collection<LoanRepaymentTransactionData> loanRepayments);
NewLoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement);
Collection<LoanRepaymentTransactionData> retrieveLoanPayments(Long loanId);
@@ -3,19 +3,21 @@ package org.mifosng.platform.loan.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosng.platform.api.NewLoanScheduleData;
import org.mifosng.platform.api.data.ClientData;
import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.DisbursementData;
import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.LoanBasicDetailsData;
import org.mifosng.platform.api.data.LoanChargeData;
import org.mifosng.platform.api.data.LoanPermissionData;
import org.mifosng.platform.api.data.LoanProductData;
import org.mifosng.platform.api.data.LoanRepaymentPeriodData;
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
import org.mifosng.platform.api.data.LoanScheduleData;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
import org.mifosng.platform.api.data.LoanTransactionData;
import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.client.service.ClientReadPlatformService;
@@ -89,17 +91,49 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
@Override
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId, final CurrencyData currency, final Collection<LoanRepaymentTransactionData> loanRepayments) {
public NewLoanScheduleData retrieveRepaymentSchedule(final Long loanId, final CurrencyData currency, final DisbursementData disbursement) {
try {
context.authenticatedUser();
LoanScheduleMapper rm = new LoanScheduleMapper();
String sql = "select " + rm.loanScheduleSchema()
+ " where l.id = ? order by ls.loan_id, ls.installment";
Collection<LoanRepaymentPeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement);
final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment";
return new LoanScheduleData(currency, repaymentSchedulePeriods, loanRepayments);
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursement(disbursement.disbursementDate(), disbursement.amount());
final Collection<LoanSchedulePeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>(repaymentSchedulePeriods.size()+1);
periods.add(disbursementPeriod);
periods.addAll(repaymentSchedulePeriods);
LoanSchedulePeriodDataWrapper wrapper = new LoanSchedulePeriodDataWrapper(periods);
final Integer loanTermInDays = null;
final BigDecimal cumulativePrincipalDisbursed = wrapper.deriveCumulativePrincipalDisbursed();
final BigDecimal cumulativePrincipalDue = wrapper.deriveCumulativePrincipalDue();
final BigDecimal cumulativePrincipalPaid = wrapper.deriveCumulativePrincipalPaid();
final BigDecimal cumulativePrincipalOutstanding = wrapper.deriveCumulativePrincipalOutstanding();
final BigDecimal cumulativeInterestExpected = wrapper.deriveCumulativeInterestExpected();
final BigDecimal cumulativeInterestPaid = wrapper.deriveCumulativeInterestPaid();
final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived();
final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding();
final BigDecimal cumulativeChargesToDate = wrapper.deriveCumulativeChargesToDate();
final BigDecimal cumulativeChargesPaid = wrapper.deriveCumulativeChargesPaid();
final BigDecimal cumulativeChargesOutstanding = wrapper.deriveCumulativeChargesOutstanding();
final BigDecimal totalCostOfLoan = cumulativeInterestExpected.add(cumulativeChargesToDate);
final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalCostOfLoan);
final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeChargesPaid);
final BigDecimal totalWaivedToDate = cumulativeInterestWaived;
final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding).add(cumulativeChargesOutstanding);
final BigDecimal totalInArrears = null;
return new NewLoanScheduleData(currency, periods, loanTermInDays,
cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalOutstanding,
cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestOutstanding,
cumulativeChargesToDate, cumulativeChargesPaid, cumulativeChargesOutstanding,
totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalOutstanding, totalInArrears);
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
}
@@ -319,7 +353,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
@Override
public LoanBasicDetailsData mapRow(final ResultSet rs, final int rowNum)
public LoanBasicDetailsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum)
throws SQLException {
String currencyCode = rs.getString("currencyCode");
@@ -423,47 +457,58 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
}
private static final class LoanScheduleMapper implements
RowMapper<LoanRepaymentPeriodData> {
private static final class LoanScheduleMapper implements RowMapper<LoanSchedulePeriodData> {
private LocalDate lastDueDate;
private BigDecimal outstandingLoanBalance;
public LoanScheduleMapper(final DisbursementData disbursement) {
this.lastDueDate = disbursement.disbursementDate();
this.outstandingLoanBalance = disbursement.amount();
}
public String loanScheduleSchema() {
return " ls.loan_id as loanId, ls.installment as period, ls.duedate as `date`, "
+ " ls.principal_amount as principal, ls.principal_completed_derived as principalPaid, "
+ " ls.interest_amount as interest, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived "
return " ls.loan_id as loanId, ls.installment as period, ls.duedate as dueDate, "
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, "
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived "
+ " from m_loan l "
+ " join m_loan_repayment_schedule ls on ls.loan_id = l.id ";
}
@Override
public LoanRepaymentPeriodData mapRow(final ResultSet rs,
final int rowNum) throws SQLException {
public LoanSchedulePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
Long loanId = rs.getLong("loanId");
Integer period = JdbcSupport.getInteger(rs, "period");
LocalDate date = JdbcSupport.getLocalDate(rs, "date");
BigDecimal principal = rs.getBigDecimal("principal");
BigDecimal principalPaid = rs.getBigDecimal("principalPaid");
BigDecimal principalOutstanding = principal.subtract(principalPaid);
BigDecimal interest = rs.getBigDecimal("interest");
BigDecimal interestPaid = rs.getBigDecimal("interestPaid");
final Long loanId = rs.getLong("loanId");
final Integer period = JdbcSupport.getInteger(rs, "period");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = rs.getBigDecimal("principalDue");
final BigDecimal principalPaid = rs.getBigDecimal("principalPaid");
final BigDecimal principalOutstanding = principalDue.subtract(principalPaid);
final BigDecimal interestDue = rs.getBigDecimal("interestDue");
final BigDecimal interestPaid = rs.getBigDecimal("interestPaid");
BigDecimal interestWaived = rs.getBigDecimal("interestWaived");
if (interestWaived == null) {
interestWaived = BigDecimal.ZERO;
}
BigDecimal interestOutstanding = interest.subtract(interestPaid).subtract(interestWaived);
final BigDecimal interestOutstanding = interestDue.subtract(interestPaid).subtract(interestWaived);
BigDecimal total = principal.add(interest);
BigDecimal totalPaid = principalPaid.add(interestPaid);
BigDecimal totalWaived = interestWaived;
BigDecimal totalOutstanding = principalOutstanding.add(interestOutstanding);
final BigDecimal totalDueForPeriod = principalDue.add(interestDue);
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid);
final BigDecimal totalWaivedForPeriod = interestWaived;
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding);
return new LoanRepaymentPeriodData(loanId, period, date,
principal, principalPaid, principalOutstanding, interest,
interestPaid, interestWaived, interestOutstanding, total, totalPaid, totalWaived,
totalOutstanding);
final LocalDate fromDate = this.lastDueDate;
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanBalance.subtract(principalDue);
// update based on current period values
this.lastDueDate = dueDate;
outstandingLoanBalance = outstandingLoanBalance.subtract(principalDue);
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate,
principalDue, principalPaid, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestDue,
interestPaid, interestWaived, interestOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod,
totalOutstandingForPeriod);
}
}
@@ -481,7 +526,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanRepaymentTransactionData mapRow(final ResultSet rs,
final int rowNum) throws SQLException {
@SuppressWarnings("unused") final int rowNum) throws SQLException {
String currencyCode = rs.getString("currencyCode");
String currencyName = rs.getString("currencyName");
@@ -0,0 +1,103 @@
package org.mifosng.platform.loan.service;
import java.math.BigDecimal;
import java.util.Collection;
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
public class LoanSchedulePeriodDataWrapper {
private final Collection<LoanSchedulePeriodData> periods;
public LoanSchedulePeriodDataWrapper(final Collection<LoanSchedulePeriodData> periods) {
this.periods = periods;
}
public BigDecimal deriveCumulativePrincipalDisbursed() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.principalDisbursed());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePrincipalDue() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.principalDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePrincipalPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.principalPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePrincipalOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.principalOutstanding());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeInterestExpected() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeInterestPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeInterestWaived() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestWaived());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeInterestOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestOutstanding());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesOutstanding());
}
return cumulativeValue;
}
}