diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 3cff30b00..51cfcf973 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -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 loanRepayments = null; - LoanScheduleData repaymentSchedule = null; + NewLoanScheduleData repaymentSchedule = null; LoanPermissionData permissions = null; Collection 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()); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/NewLoanScheduleData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/NewLoanScheduleData.java index dd090db1b..e4f341fb6 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/NewLoanScheduleData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/NewLoanScheduleData.java @@ -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 principalDue 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 principalDisbursed and totalCostOfLoan. + * The sum of principalDisbursed and totalCostOfLoan. (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; /** * periods 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 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 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 getPeriods() { return periods; } + + public BigDecimal totalOutstanding() { + return this.totalOutstanding; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DisbursementData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DisbursementData.java new file mode 100644 index 000000000..ae637c4b2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/DisbursementData.java @@ -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; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java index 38b9a45fd..509476f33 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountData.java @@ -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 loanRepayments; private final Collection 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 loanRepayments, final LoanPermissionData permissions, final Collection charges, diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountSummaryData.java index 4ece0c3f4..0d62b8fd4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanAccountSummaryData.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java index e832d00b3..55faf57b4 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanBasicDetailsData.java @@ -508,4 +508,8 @@ public class LoanBasicDetailsData { public String getLoanOfficerName() { return loanOfficerName; } + + public DisbursementData toDisburementData() { + return new DisbursementData(this.expectedDisbursementDate, this.actualDisbursementDate, this.principal); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentPeriodData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentPeriodData.java deleted file mode 100644 index 637176333..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentPeriodData.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanScheduleData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanScheduleData.java deleted file mode 100644 index 766471f39..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanScheduleData.java +++ /dev/null @@ -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 repaymentPeriods; - - public LoanScheduleData(final CurrencyData currency, final Collection repaymentPeriods, final Collection loanRepayments) { - this.summary = derivedSummaryFrom(currency, repaymentPeriods, loanRepayments); - this.currency = currency; - this.repaymentPeriods = repaymentPeriods; - } - - private LoanAccountSummaryData derivedSummaryFrom( - final CurrencyData currency, - final Collection repaymentSchedule, - final Collection 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(); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java index 29147259f..d7b2902e7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanSchedulePeriodData.java @@ -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); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java index 2aa6bfd54..9216c09cf 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java @@ -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 loanRepayments); + NewLoanScheduleData retrieveRepaymentSchedule(Long loanId, CurrencyData currency, DisbursementData disbursement); Collection retrieveLoanPayments(Long loanId); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java index 93b43f595..58964f6ea 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java @@ -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 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 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 repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); + + final Collection periods = new ArrayList(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 { + private static final class LoanScheduleMapper implements RowMapper { + + 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"); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java new file mode 100644 index 000000000..92d38e482 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java @@ -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 periods; + + public LoanSchedulePeriodDataWrapper(final Collection 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; + } +} \ No newline at end of file