From a6a74a401b78e7f5bfc79d7bb4efe71dbda6f878 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Wed, 19 Sep 2012 17:13:26 +0100 Subject: [PATCH] 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. --- .../platform/api/LoansApiResource.java | 7 +- .../platform/api/NewLoanScheduleData.java | 88 +++++++++- .../platform/api/data/DisbursementData.java | 33 ++++ .../platform/api/data/LoanAccountData.java | 5 +- .../api/data/LoanAccountSummaryData.java | 1 + .../api/data/LoanBasicDetailsData.java | 4 + .../api/data/LoanRepaymentPeriodData.java | 97 ----------- .../platform/api/data/LoanScheduleData.java | 94 ----------- .../api/data/LoanSchedulePeriodData.java | 150 +++++++++++++++++- .../loan/service/LoanReadPlatformService.java | 5 +- .../service/LoanReadPlatformServiceImpl.java | 117 +++++++++----- .../LoanSchedulePeriodDataWrapper.java | 103 ++++++++++++ 12 files changed, 458 insertions(+), 246 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/DisbursementData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanRepaymentPeriodData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanScheduleData.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanSchedulePeriodDataWrapper.java 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