MIFOSX-175: WIP - adding loan summary fields to loan table and resultant refactor of loan schedule and loan summary retrieval code.

This commit is contained in:
Keith Woodlock
2013-02-27 01:31:18 +00:00
parent 0bfaf2cbaf
commit 998c9bb3b2
24 changed files with 1864 additions and 1697 deletions
@@ -540,7 +540,6 @@ CREATE TABLE `m_loan` (
`repay_every` smallint(5) NOT NULL,
`repayment_period_frequency_enum` smallint(5) NOT NULL,
`amortization_method_enum` smallint(5) NOT NULL,
`total_charges_due_at_disbursement_derived` decimal(19,6) DEFAULT NULL,
`interest_calculated_from_date` date DEFAULT NULL,
`expected_disbursedon_date` date DEFAULT NULL,
`expected_firstrepaymenton_date` date DEFAULT NULL,
@@ -560,6 +559,30 @@ CREATE TABLE `m_loan` (
`writtenoffon_date` date DEFAULT NULL,
`closedon_date` date DEFAULT NULL,
`closedon_userid` bigint(20) DEFAULT NULL,
`total_charges_due_at_disbursement_derived` decimal(19,6) DEFAULT NULL,
`principal_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`principal_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`principal_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`principal_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`interest_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`fee_charges_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`penalty_charges_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0,
`first_overdue_installment_date_derived` DATE NULL,
`createdby_id` bigint(20) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
ALTER TABLE `m_loan`
DROP COLUMN `lastmodifiedby_id`,
DROP COLUMN `lastmodified_date`,
DROP COLUMN `created_date`,
DROP COLUMN `createdby_id`;
ALTER TABLE `m_loan`
CHANGE COLUMN `total_charges_due_at_disbursement_derived` `total_charges_due_at_disbursement_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `closedon_userid` ;
ALTER TABLE `m_loan`
ADD COLUMN `principal_disbursed_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_charges_due_at_disbursement_derived` ,
ADD COLUMN `principal_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `principal_disbursed_derived` ,
ADD COLUMN `principal_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `principal_repaid_derived` ,
ADD COLUMN `principal_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `principal_writtenoff_derived`,
ADD COLUMN `principal_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `principal_outstanding_derived`,
ADD COLUMN `interest_charged_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `principal_overdue_derived` ,
ADD COLUMN `interest_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_charged_derived` ,
ADD COLUMN `interest_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_repaid_derived` ,
ADD COLUMN `interest_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_waived_derived` ,
ADD COLUMN `interest_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_writtenoff_derived`,
ADD COLUMN `interest_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_outstanding_derived`,
ADD COLUMN `fee_charges_charged_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `interest_overdue_derived` ,
ADD COLUMN `fee_charges_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_charged_derived` ,
ADD COLUMN `fee_charges_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_repaid_derived` ,
ADD COLUMN `fee_charges_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_waived_derived` ,
ADD COLUMN `fee_charges_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_writtenoff_derived`,
ADD COLUMN `fee_charges_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_outstanding_derived`,
ADD COLUMN `penalty_charges_charged_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `fee_charges_overdue_derived` ,
ADD COLUMN `penalty_charges_repaid_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_charged_derived` ,
ADD COLUMN `penalty_charges_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_repaid_derived` ,
ADD COLUMN `penalty_charges_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_waived_derived`,
ADD COLUMN `penalty_charges_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_writtenoff_derived`,
ADD COLUMN `penalty_charges_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_outstanding_derived`,
ADD COLUMN `total_expected_repayment_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `penalty_charges_overdue_derived`,
ADD COLUMN `total_repayment_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_expected_repayment_derived`,
ADD COLUMN `total_expected_costofloan_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_repayment_derived`,
ADD COLUMN `total_costofloan_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_expected_costofloan_derived`,
ADD COLUMN `total_waived_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_costofloan_derived`,
ADD COLUMN `total_writtenoff_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_waived_derived`,
ADD COLUMN `total_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_writtenoff_derived`,
ADD COLUMN `total_overdue_derived` DECIMAL(19,6) NOT NULL DEFAULT 0.0 AFTER `total_outstanding_derived`,
ADD COLUMN `overdue_since_date_derived` DATE NULL AFTER `total_outstanding_derived`;
@@ -16,46 +16,60 @@ import org.joda.time.LocalDate;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Support for retrieving possibly null values from jdbc recordset delegating to springs {@link JdbcUtils} where possible.
* Support for retrieving possibly null values from jdbc recordset delegating to
* springs {@link JdbcUtils} where possible.
*/
public class JdbcSupport {
public static DateTime getDateTime(ResultSet rs, String columnName) throws SQLException {
DateTime dateTime = null;
Timestamp dateValue = rs.getTimestamp(columnName);
if (dateValue != null) {
dateTime = new DateTime(dateValue.getTime());
}
return dateTime;
}
public static LocalDate getLocalDate(final ResultSet rs, final String columnName) throws SQLException {
LocalDate localDate = null;
Date dateValue = rs.getDate(columnName);
if (dateValue != null) {
localDate = new LocalDate(dateValue);
}
return localDate;
}
public static DateTime getDateTime(final ResultSet rs, final String columnName) throws SQLException {
DateTime dateTime = null;
Timestamp dateValue = rs.getTimestamp(columnName);
if (dateValue != null) {
dateTime = new DateTime(dateValue.getTime());
}
return dateTime;
}
public static Long getLong(final ResultSet rs, final String columnName) throws SQLException {
return (Long) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Long.class);
}
public static LocalDate getLocalDate(final ResultSet rs, final String columnName) throws SQLException {
LocalDate localDate = null;
final Date dateValue = rs.getDate(columnName);
if (dateValue != null) {
localDate = new LocalDate(dateValue);
}
return localDate;
}
public static Integer getInteger(final ResultSet rs, final String columnName) throws SQLException {
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
}
public static Long getLong(final ResultSet rs, final String columnName) throws SQLException {
return (Long) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Long.class);
}
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
final BigDecimal value = rs.getBigDecimal(columnName);
return defaultToZeroIfNull(value);
}
private static BigDecimal defaultToZeroIfNull(final BigDecimal value) {
BigDecimal result = BigDecimal.ZERO;
if (value != null) {
result = value;
}
return result;
}
public static Integer getInteger(final ResultSet rs, final String columnName) throws SQLException {
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
}
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
final BigDecimal value = rs.getBigDecimal(columnName);
return defaultToZeroIfNull(value);
}
private static BigDecimal defaultToZeroIfNull(final BigDecimal value) {
BigDecimal result = BigDecimal.ZERO;
if (value != null) {
result = value;
}
return result;
}
public static BigDecimal getBigDecimalDefaultToNullIfZero(final ResultSet rs, final String columnName) throws SQLException {
final BigDecimal value = rs.getBigDecimal(columnName);
return defaultToNullIfZero(value);
}
private static BigDecimal defaultToNullIfZero(final BigDecimal value) {
BigDecimal result = value;
if (BigDecimal.ZERO.compareTo(value) == 0) {
result = null;
}
return result;
}
}
@@ -81,9 +81,9 @@ public class LoansApiResource {
"inArrearsTolerance", "termFrequency", "termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery",
"interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyId",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"expectedFirstRepaymentOnDate", "interestChargedFromDate", "timeline", "totalFeeChargesAtDisbursement", "repaymentSchedule",
"transactions", "charges", "collateral", "guarantors", "productOptions", "amortizationTypeOptions", "interestTypeOptions",
"interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions", "termFrequencyTypeOptions",
"expectedFirstRepaymentOnDate", "interestChargedFromDate", "timeline", "totalFeeChargesAtDisbursement", "summary",
"repaymentSchedule", "transactions", "charges", "collateral", "guarantors", "productOptions", "amortizationTypeOptions",
"interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions", "termFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions",
"loanPurposeOptions", "loanCollateralOptions", "chargeTemplate"));
@@ -146,7 +146,7 @@ public class LoansApiResource {
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String templateForNewLoanApplication(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -217,7 +217,7 @@ public class LoansApiResource {
@Path("{loanId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveLoanAccountDetails(@PathParam("loanId") final Long loanId, @Context final UriInfo uriInfo) {
public String retrieveLoan(@PathParam("loanId") final Long loanId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -9,6 +9,8 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Transient;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -69,7 +71,7 @@ public class LoanAccountData {
// timeline
private final LoanApplicationTimelineData timeline;
// totals
private final BigDecimal totalFeeChargesAtDisbursement;
private final LoanSummaryData summary;
// associations
private final LoanScheduleData repaymentSchedule;
@@ -95,6 +97,9 @@ public class LoanAccountData {
private final ChargeData chargeTemplate;
@Transient
private BigDecimal feeChargesAtDisbursementCharged;
/**
* Used to produce a {@link LoanAccountData} with only collateral options
* for now.
@@ -137,7 +142,8 @@ public class LoanAccountData {
final LocalDate expectedFirstRepaymentOnDate = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = null;
final BigDecimal totalFeeChargesAtDisbursement = null;
final LoanSummaryData summary = null;
final BigDecimal feeChargesDueAtDisbursementCharged = null;
final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null;
@@ -163,11 +169,11 @@ public class LoanAccountData {
loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, amortizationType,
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, totalFeeChargesAtDisbursement, repaymentSchedule,
transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
loanCollateralOptions);
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged,
repaymentSchedule, transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions,
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions);
}
/**
@@ -210,7 +216,8 @@ public class LoanAccountData {
final LocalDate expectedFirstRepaymentOnDate = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = LoanApplicationTimelineData.templateDefault(expectedDisbursementDate);
final BigDecimal totalFeeChargesAtDisbursement = null;
final LoanSummaryData summary = null;
final BigDecimal feeChargesDueAtDisbursementCharged = null;
final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null;
@@ -237,11 +244,11 @@ public class LoanAccountData {
loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, amortizationType,
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, totalFeeChargesAtDisbursement, repaymentSchedule,
transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
loanCollateralOptions);
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged,
repaymentSchedule, transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions,
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions);
}
/**
@@ -285,7 +292,8 @@ public class LoanAccountData {
final LocalDate expectedFirstRepaymentOnDate = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = LoanApplicationTimelineData.templateDefault(expectedDisbursementDate);
final BigDecimal totalFeeChargesAtDisbursement = null;
final LoanSummaryData summary = null;
final BigDecimal feeChargesDueAtDisbursementCharged = null;
final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null;
@@ -312,11 +320,11 @@ public class LoanAccountData {
loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionProcessingStrategyId, amortizationType,
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, totalFeeChargesAtDisbursement, repaymentSchedule,
transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
loanCollateralOptions);
expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged,
repaymentSchedule, transactions, charges, collateral, guarantors, productOptions, termFrequencyTypeOptions,
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
loanPurposeOptions, loanCollateralOptions);
}
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -357,10 +365,10 @@ public class LoanAccountData {
product.getTransactionProcessingStrategyId(), product.getAmortizationType(), product.getInterestRatePerPeriod(),
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, acc.interestChargedFromDate, acc.timeline,
acc.totalFeeChargesAtDisbursement, repaymentSchedule, transactions, charges, collateral, guarantors, productOptions,
termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions);
acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors,
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions);
}
/*
@@ -378,7 +386,8 @@ public class LoanAccountData {
final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType, final BigDecimal annualInterestRate,
final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType,
final LocalDate expectedFirstRepaymentOnDate, final LocalDate interestChargedFromDate,
final LoanApplicationTimelineData timeline, final BigDecimal totalFeeChargesAtDisbursement) {
final LoanApplicationTimelineData timeline, final LoanSummaryData loanSummary,
final BigDecimal feeChargesDueAtDisbursementCharged) {
final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null;
@@ -405,10 +414,11 @@ public class LoanAccountData {
loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, amortizationType, interestRatePerPeriod,
interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
interestChargedFromDate, timeline, totalFeeChargesAtDisbursement, repaymentSchedule, transactions, charges, collateral,
guarantors, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions);
interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions,
charges, collateral, guarantors, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
loanCollateralOptions);
}
/*
@@ -433,10 +443,10 @@ public class LoanAccountData {
acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType,
acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, acc.interestChargedFromDate, acc.timeline,
acc.totalFeeChargesAtDisbursement, repaymentSchedule, transactions, charges, collateral, guarantors, productOptions,
termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions,
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions);
acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors,
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions);
}
private LoanAccountData(
@@ -477,11 +487,12 @@ public class LoanAccountData {
final LocalDate expectedFirstRepaymentOnDate,
final LocalDate interestChargedFromDate,
final LoanApplicationTimelineData timeline,
final BigDecimal totalFeeChargesAtDisbursement, //
final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionData> transactions,
final Collection<LoanChargeData> charges, final Collection<LoanCollateralData> collateral,
final Collection<GuarantorData> guarantors, final Collection<LoanProductData> productOptions,
final Collection<EnumOptionData> termFrequencyTypeOptions, final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final LoanSummaryData summary, //
final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule,
final Collection<LoanTransactionData> transactions, final Collection<LoanChargeData> charges,
final Collection<LoanCollateralData> collateral, final Collection<GuarantorData> guarantors,
final Collection<LoanProductData> productOptions, final Collection<EnumOptionData> termFrequencyTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final Collection<TransactionProcessingStrategyData> repaymentStrategyOptions,
final Collection<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<EnumOptionData> amortizationTypeOptions,
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
@@ -527,7 +538,7 @@ public class LoanAccountData {
this.timeline = timeline;
// totals
this.totalFeeChargesAtDisbursement = totalFeeChargesAtDisbursement;
this.summary = summary;
// associations
this.repaymentSchedule = repaymentSchedule;
@@ -555,7 +566,7 @@ public class LoanAccountData {
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
return this.timeline.repaymentScheduleRelatedData(this.currency, this.principal, this.inArrearsTolerance,
this.totalFeeChargesAtDisbursement);
this.feeChargesAtDisbursementCharged);
}
public Long officeId() {
@@ -0,0 +1,101 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.data;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
/**
* Immutable data object representing loan summary information.
*/
@SuppressWarnings("unused")
public class LoanSummaryData {
private final CurrencyData currency;
private final BigDecimal principalDisbursed;
private final BigDecimal principalPaid;
private final BigDecimal principalWrittenOff;
private final BigDecimal principalOutstanding;
private final BigDecimal principalOverdue;
private final BigDecimal interestCharged;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding;
private final BigDecimal interestOverdue;
private final BigDecimal feeChargesCharged;
private final BigDecimal feeChargesDueAtDisbursementCharged;
private final BigDecimal feeChargesPaid;
private final BigDecimal feeChargesWaived;
private final BigDecimal feeChargesWrittenOff;
private final BigDecimal feeChargesOutstanding;
private final BigDecimal feeChargesOverdue;
private final BigDecimal penaltyChargesCharged;
private final BigDecimal penaltyChargesPaid;
private final BigDecimal penaltyChargesWaived;
private final BigDecimal penaltyChargesWrittenOff;
private final BigDecimal penaltyChargesOutstanding;
private final BigDecimal penaltyChargesOverdue;
private final BigDecimal totalExpectedRepayment;
private final BigDecimal totalRepayment;
private final BigDecimal totalExpectedCostOfLoan;
private final BigDecimal totalCostOfLoan;
private final BigDecimal totalWaived;
private final BigDecimal totalWrittenOff;
private final BigDecimal totalOutstanding;
private final BigDecimal totalOverdue;
private final LocalDate overdueSinceDate;
public LoanSummaryData(final CurrencyData currency, final BigDecimal principalDisbursed, final BigDecimal principalPaid,
final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding, final BigDecimal principalOverdue,
final BigDecimal interestCharged, final BigDecimal interestPaid, final BigDecimal interestWaived,
final BigDecimal interestWrittenOff, final BigDecimal interestOutstanding, final BigDecimal interestOverdue,
final BigDecimal feeChargesCharged, final BigDecimal feeChargesDueAtDisbursementCharged, final BigDecimal feeChargesPaid,
final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesOutstanding,
final BigDecimal feeChargesOverdue, final BigDecimal penaltyChargesCharged, final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived, final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding,
final BigDecimal penaltyChargesOverdue, final BigDecimal totalExpectedRepayment, final BigDecimal totalRepayment,
final BigDecimal totalExpectedCostOfLoan, final BigDecimal totalCostOfLoan, final BigDecimal totalWaived,
final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding, final BigDecimal totalOverdue,
final LocalDate overdueSinceDate) {
this.currency = currency;
this.principalDisbursed = principalDisbursed;
this.principalPaid = principalPaid;
this.principalWrittenOff = principalWrittenOff;
this.principalOutstanding = principalOutstanding;
this.principalOverdue = principalOverdue;
this.interestCharged = interestCharged;
this.interestPaid = interestPaid;
this.interestWaived = interestWaived;
this.interestWrittenOff = interestWrittenOff;
this.interestOutstanding = interestOutstanding;
this.interestOverdue = interestOverdue;
this.feeChargesCharged = feeChargesCharged;
this.feeChargesDueAtDisbursementCharged = feeChargesDueAtDisbursementCharged;
this.feeChargesPaid = feeChargesPaid;
this.feeChargesWaived = feeChargesWaived;
this.feeChargesWrittenOff = feeChargesWrittenOff;
this.feeChargesOutstanding = feeChargesOutstanding;
this.feeChargesOverdue = feeChargesOverdue;
this.penaltyChargesCharged = penaltyChargesCharged;
this.penaltyChargesPaid = penaltyChargesPaid;
this.penaltyChargesWaived = penaltyChargesWaived;
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
this.penaltyChargesOutstanding = penaltyChargesOutstanding;
this.penaltyChargesOverdue = penaltyChargesOverdue;
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalRepayment = totalRepayment;
this.totalExpectedCostOfLoan = totalExpectedCostOfLoan;
this.totalCostOfLoan = totalCostOfLoan;
this.totalWaived = totalWaived;
this.totalWrittenOff = totalWrittenOff;
this.totalOutstanding = totalOutstanding;
this.totalOverdue = totalOverdue;
this.overdueSinceDate = overdueSinceDate;
}
}
@@ -46,7 +46,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
// re-process loan charges over repayment periods (picking up on waived
// loan charges)
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(currency, disbursementDate, installments, charges);
for (LoanTransaction loanTransaction : transactionsPostDisbursement) {
@@ -84,7 +84,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
if (currentInstallment.isPrincipalCompleted(currency)) {
// FIXME - KW - if auto waiving interest need to create
// another transaction to handle this.
currentInstallment.waiveInterestComponent(currentInstallment.getInterest(currency));
currentInstallment.waiveInterestComponent(currentInstallment.getInterestCharged(currency));
}
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
@@ -40,7 +40,6 @@ import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -73,11 +72,12 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingS
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") })
public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public class Loan extends AbstractPersistable<Long> {
@Column(name = "account_no", length = 20, unique = true, nullable = false)
private String accountNumber;
@@ -135,6 +135,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "submittedon_userid", nullable = true)
private AppUser submittedBy;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "rejectedon_date")
private Date rejectedOnDate;
@@ -176,6 +177,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "disbursedon_userid", nullable = true)
private AppUser disbursedBy;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "closedon_date")
private Date closedOnDate;
@@ -185,6 +187,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@JoinColumn(name = "closedon_userid", nullable = true)
private AppUser closedBy;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "writtenoffon_date")
private Date writtenOffOnDate;
@@ -193,10 +196,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "rescheduledon_date")
private Date rescheduledOnDate;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "expected_maturedon_date")
private Date expectedMaturityDate;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "maturedon_date")
private Date actualMaturityDate;
@@ -234,14 +239,123 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true)
private final List<LoanTransaction> loanTransactions = new ArrayList<LoanTransaction>();
// derived totals fields
@Column(name = "principal_disbursed_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalDisbursed;
@Column(name = "principal_repaid_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalRepaid;
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalWrittenOff;
@Column(name = "principal_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalOutstanding;
@Column(name = "principal_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPrincipalOverdue;
@Column(name = "interest_charged_derived", scale = 6, precision = 19)
private BigDecimal totalInterestCharged;
@Column(name = "interest_repaid_derived", scale = 6, precision = 19)
private BigDecimal totalInterestRepaid;
@Column(name = "interest_waived_derived", scale = 6, precision = 19)
private BigDecimal totalInterestWaived;
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal totalInterestWrittenOff;
@Column(name = "interest_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalInterestOutstanding;
@Column(name = "interest_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalInterestOverdue;
@Column(name = "fee_charges_charged_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesCharged;
@Column(name = "total_charges_due_at_disbursement_derived", scale = 6, precision = 19)
private BigDecimal totalChargesDueAtDisbursement;
@Transient
private boolean accountNumberRequiresAutoGeneration = false;
@Column(name = "fee_charges_repaid_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesRepaid;
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesWaived;
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesWrittenOff;
@Column(name = "fee_charges_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesOutstanding;
@Column(name = "fee_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalFeeChargesOverdue;
@Column(name = "penalty_charges_charged_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesCharged;
@Column(name = "penalty_charges_repaid_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesRepaid;
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesWaived;
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesWrittenOff;
@Column(name = "penalty_charges_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesOutstanding;
@Column(name = "penalty_charges_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalPenaltyChargesOverdue;
@SuppressWarnings("unused")
@Column(name = "total_expected_repayment_derived", scale = 6, precision = 19)
private BigDecimal totalExpectedRepayment;
@SuppressWarnings("unused")
@Column(name = "total_repayment_derived", scale = 6, precision = 19)
private BigDecimal totalRepayment;
@SuppressWarnings("unused")
@Column(name = "total_expected_costofloan_derived", scale = 6, precision = 19)
private BigDecimal totalExpectedCostOfLoan;
@SuppressWarnings("unused")
@Column(name = "total_costofloan_derived", scale = 6, precision = 19)
private BigDecimal totalCostOfLoan;
@SuppressWarnings("unused")
@Column(name = "total_waived_derived", scale = 6, precision = 19)
private BigDecimal totalWaived;
@SuppressWarnings("unused")
@Column(name = "total_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal totalWrittenOff;
@Column(name = "total_outstanding_derived", scale = 6, precision = 19)
private BigDecimal totalOutstanding;
@SuppressWarnings("unused")
@Column(name = "total_overdue_derived", scale = 6, precision = 19)
private BigDecimal totalOverdue;
@SuppressWarnings("unused")
@Temporal(TemporalType.DATE)
@Column(name = "overdue_since_date_derived")
private Date overdueSinceDate;
@Transient
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
private boolean accountNumberRequiresAutoGeneration = false;
@Transient
private LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
@Transient
private LoanLifecycleStateMachine loanLifecycleStateMachine;
@Transient
private LoanSummaryWrapper loanSummaryWrapper;
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final LoanProduct loanProduct,
final Fund fund, final Staff officer, final CodeValue loanPurpose,
@@ -385,7 +499,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
updateTotalChargesDueAtDisbursement();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
if (!loanCharge.isDueAtDisbursement()) {
@@ -394,9 +508,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges());
} else {
// just reprocess the loan schedule only for now.
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges());
}
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
}
private void validateLoanIsNotClosed(final LoanCharge loanCharge) {
@@ -443,13 +559,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) {
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges());
}
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
}
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final LoanCharge loanCharge) {
@@ -491,7 +609,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
updateTotalChargesDueAtDisbursement();
}
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
if (!loanCharge.isDueAtDisbursement()) {
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
@@ -499,10 +617,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges());
} else {
// reprocess loan schedule based on charge been waived.
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges());
}
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
return actualChanges;
}
@@ -534,7 +654,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
// Waive of charges whose due date falls after latest 'repayment'
// transaction dont require entire loan schedule to be reprocessed.
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) {
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
@@ -542,10 +662,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges());
} else {
// reprocess loan schedule based on charge been waived.
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges());
}
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
doPostLoanTransactionChecks(waiveLoanChargeTransaction.getTransactionDate(), loanLifecycleStateMachine);
return waiveLoanChargeTransaction;
@@ -628,17 +750,155 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
// already against it - then need to re-process it
final List<LoanTransaction> repaymentsOrWaivers = retreiveListOfTransactionsPostDisbursement();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), repaymentsOrWaivers, getCurrency(),
this.repaymentScheduleInstallments, setOfLoanCharges());
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
}
public void updateLoanScheduleDependentDerivedFields() {
private void updateLoanScheduleDependentDerivedFields() {
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
this.actualMaturityDate = determineExpectedMaturityDate().toDate();
}
private void updateLoanSummaryDerivedFields(final LoanSummaryWrapper summary) {
final Money principal = this.loanRepaymentScheduleDetail.getPrincipal();
final MonetaryCurrency currency = loanCurrency();
if (isNotDisbursed()) {
this.totalPrincipalDisbursed = BigDecimal.ZERO;
this.totalPrincipalRepaid = BigDecimal.ZERO;
this.totalPrincipalWrittenOff = BigDecimal.ZERO;
this.totalPrincipalOutstanding = BigDecimal.ZERO;
this.totalPrincipalOverdue = BigDecimal.ZERO;
this.totalInterestCharged = BigDecimal.ZERO;
this.totalInterestRepaid = BigDecimal.ZERO;
this.totalInterestWaived = BigDecimal.ZERO;
this.totalInterestWrittenOff = BigDecimal.ZERO;
this.totalInterestOutstanding = BigDecimal.ZERO;
this.totalInterestOverdue = BigDecimal.ZERO;
this.totalFeeChargesCharged = BigDecimal.ZERO;
this.totalFeeChargesRepaid = BigDecimal.ZERO;
this.totalFeeChargesWaived = BigDecimal.ZERO;
this.totalFeeChargesWrittenOff = BigDecimal.ZERO;
this.totalFeeChargesOutstanding = BigDecimal.ZERO;
this.totalFeeChargesOverdue = BigDecimal.ZERO;
this.totalPenaltyChargesCharged = BigDecimal.ZERO;
this.totalPenaltyChargesRepaid = BigDecimal.ZERO;
this.totalPenaltyChargesWaived = BigDecimal.ZERO;
this.totalPenaltyChargesWrittenOff = BigDecimal.ZERO;
this.totalPenaltyChargesOutstanding = BigDecimal.ZERO;
this.totalPenaltyChargesOverdue = BigDecimal.ZERO;
this.totalExpectedRepayment = BigDecimal.ZERO;
this.totalRepayment = BigDecimal.ZERO;
this.totalExpectedCostOfLoan = BigDecimal.ZERO;
this.totalCostOfLoan = BigDecimal.ZERO;
this.totalWaived = BigDecimal.ZERO;
this.totalWrittenOff = BigDecimal.ZERO;
this.totalOutstanding = BigDecimal.ZERO;
this.totalOverdue = BigDecimal.ZERO;
this.overdueSinceDate = null;
} else {
this.totalPrincipalDisbursed = principal.getAmount();
this.totalPrincipalRepaid = summary.calculateTotalPrincipalRepaid(this.repaymentScheduleInstallments, currency).getAmount();
this.totalPrincipalWrittenOff = summary.calculateTotalPrincipalWrittenOff(this.repaymentScheduleInstallments, currency)
.getAmount();
this.totalPrincipalOutstanding = principal.minus(totalPrincipalRepaid).minus(totalPrincipalWrittenOff).getAmount();
this.totalPrincipalOverdue = summary.calculateTotalPrincipalOverdueOn(this.repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
final Money totalInterestCharged = summary.calculateTotalInterestCharged(this.repaymentScheduleInstallments, currency);
this.totalInterestCharged = totalInterestCharged.getAmount();
this.totalInterestRepaid = summary.calculateTotalInterestRepaid(this.repaymentScheduleInstallments, currency).getAmount();
this.totalInterestWaived = summary.calculateTotalInterestWaived(this.repaymentScheduleInstallments, currency).getAmount();
this.totalInterestWrittenOff = summary.calculateTotalInterestWrittenOff(this.repaymentScheduleInstallments, currency)
.getAmount();
if (totalInterestCharged.isGreaterThanZero()) {
this.totalInterestOutstanding = totalInterestCharged.minus(this.totalInterestRepaid).minus(this.totalInterestWaived)
.minus(this.totalInterestWrittenOff).getAmount();
this.totalInterestOverdue = summary.calculateTotalInterestOverdueOn(this.repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalFeeChargesCharged = summary.calculateTotalFeeChargesCharged(this.repaymentScheduleInstallments, currency);
this.totalFeeChargesCharged = totalFeeChargesCharged.getAmount();
this.totalFeeChargesRepaid = summary.calculateTotalFeeChargesRepaid(this.repaymentScheduleInstallments, currency).getAmount();
this.totalFeeChargesWaived = summary.calculateTotalFeeChargesWaived(this.repaymentScheduleInstallments, currency).getAmount();
this.totalFeeChargesWrittenOff = summary.calculateTotalFeeChargesWrittenOff(this.repaymentScheduleInstallments, currency)
.getAmount();
if (totalFeeChargesCharged.isGreaterThanZero()) {
this.totalFeeChargesOutstanding = totalFeeChargesCharged.minus(this.totalFeeChargesRepaid)
.minus(this.totalFeeChargesWaived).minus(this.totalFeeChargesWrittenOff).getAmount();
this.totalFeeChargesOverdue = summary.calculateTotalFeeChargesOverdueOn(this.repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalPenaltyChargesCharged = summary.calculateTotalPenaltyChargesCharged(this.repaymentScheduleInstallments,
currency);
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged.getAmount();
this.totalPenaltyChargesRepaid = summary.calculateTotalPenaltyChargesRepaid(this.repaymentScheduleInstallments, currency)
.getAmount();
this.totalPenaltyChargesWaived = summary.calculateTotalPenaltyChargesWaived(this.repaymentScheduleInstallments, currency)
.getAmount();
this.totalPenaltyChargesWrittenOff = summary.calculateTotalPenaltyChargesWrittenOff(this.repaymentScheduleInstallments,
currency).getAmount();
if (totalPenaltyChargesCharged.isGreaterThanZero()) {
this.totalPenaltyChargesOutstanding = totalPenaltyChargesCharged.minus(this.totalPenaltyChargesRepaid)
.minus(this.totalPenaltyChargesWaived).minus(this.totalPenaltyChargesWrittenOff).getAmount();
this.totalPenaltyChargesOverdue = summary.calculateTotalPenaltyChargesOverdueOn(this.repaymentScheduleInstallments,
currency, DateUtils.getLocalDateOfTenant()).getAmount();
}
final Money totalExpectedRepayment = Money.of(currency, this.totalPrincipalDisbursed).plus(this.totalInterestCharged)
.plus(this.totalFeeChargesCharged).plus(this.totalPenaltyChargesCharged);
this.totalExpectedRepayment = totalExpectedRepayment.getAmount();
final Money totalRepayment = Money.of(currency, this.totalPrincipalRepaid).plus(this.totalInterestRepaid)
.plus(this.totalFeeChargesRepaid).plus(this.totalPenaltyChargesRepaid);
this.totalRepayment = totalRepayment.getAmount();
final Money totalExpectedCostOfLoan = Money.of(currency, this.totalInterestCharged).plus(this.totalFeeChargesCharged)
.plus(this.totalPenaltyChargesCharged);
this.totalExpectedCostOfLoan = totalExpectedCostOfLoan.getAmount();
final Money totalCostOfLoan = Money.of(currency, this.totalInterestRepaid).plus(this.totalFeeChargesRepaid)
.plus(this.totalPenaltyChargesRepaid);
this.totalCostOfLoan = totalCostOfLoan.getAmount();
final Money totalWaived = Money.of(currency, this.totalInterestWaived).plus(this.totalFeeChargesWaived)
.plus(this.totalPenaltyChargesWaived);
this.totalWaived = totalWaived.getAmount();
final Money totalWrittenOff = Money.of(currency, this.totalPrincipalWrittenOff).plus(this.totalInterestWrittenOff)
.plus(this.totalFeeChargesWrittenOff).plus(this.totalPenaltyChargesWrittenOff);
this.totalWrittenOff = totalWrittenOff.getAmount();
final Money totalOutstanding = Money.of(currency, this.totalPrincipalOutstanding).plus(this.totalInterestOutstanding)
.plus(this.totalFeeChargesOutstanding).plus(this.totalPenaltyChargesOutstanding);
this.totalOutstanding = totalOutstanding.getAmount();
final Money totalOverdue = Money.of(currency, this.totalPrincipalOverdue).plus(this.totalInterestOverdue)
.plus(this.totalFeeChargesOverdue).plus(this.totalPenaltyChargesOverdue);
this.totalOverdue = totalOverdue.getAmount();
final LocalDate overdueSinceLocalDate = summary.determineOverdueSinceDateFrom(this.repaymentScheduleInstallments, currency,
DateUtils.getLocalDateOfTenant());
if (overdueSinceLocalDate != null) {
this.overdueSinceDate = overdueSinceLocalDate.toDate();
} else {
this.overdueSinceDate = null;
}
}
}
public Map<String, Object> loanApplicationModification(final JsonCommand command, final Set<LoanCharge> possiblyModifedLoanCharges,
final Set<LoanCollateral> possiblyModifedLoanCollateralItems, final AprCalculator aprCalculator) {
@@ -1100,13 +1360,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return ids;
}
public Map<String, Object> disburse(final AppUser currentUser, final JsonCommand command,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ApplicationCurrency currency,
public Map<String, Object> disburse(final AppUser currentUser, final JsonCommand command, final ApplicationCurrency currency,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
updateLoanToPreDisbursalState();
updateLoanToPreDisbursalState(this.loanSummaryWrapper);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
@@ -1131,6 +1390,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
regenerateRepaymentSchedule(currency);
} else {
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
}
}
@@ -1164,8 +1425,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
final LoanSchedule loanSchedule = new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod,
interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency,
loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(),
this.getExpectedFirstRepaymentOnDate(), this.getInterestChargedFromDate(), inArrearsTolerance);
loanTermPeriodFrequencyType, setOfLoanCharges(), this.getDisbursementDate(), this.getExpectedFirstRepaymentOnDate(),
this.getInterestChargedFromDate(), inArrearsTolerance);
final LoanScheduleData generatedData = loanSchedule.generate();
@@ -1219,13 +1480,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return disbursementTransaction;
}
public Map<String, Object> undoDisbursal(final LoanLifecycleStateMachine loanLifecycleStateMachine,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
public Map<String, Object> undoDisbursal(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
@@ -1239,7 +1499,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
reverseExistingTransactions();
updateLoanToPreDisbursalState();
updateLoanToPreDisbursalState(this.loanSummaryWrapper);
}
return actualChanges;
@@ -1252,7 +1512,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
}
}
private void updateLoanToPreDisbursalState() {
private void updateLoanToPreDisbursalState(final LoanSummaryWrapper summaryWrapper) {
this.actualDisbursementDate = null;
for (LoanCharge charge : setOfLoanCharges()) {
@@ -1263,8 +1523,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
currentInstallment.resetDerivedComponents();
}
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, setOfLoanCharges());
updateLoanSummaryDerivedFields(summaryWrapper);
}
public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -1340,7 +1602,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
}
}
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
if (isTransactionChronologicallyLatest && adjustedTransaction == null) {
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments,
@@ -1351,6 +1613,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges());
}
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
}
@@ -1529,31 +1793,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
}
private boolean isRepaidInFull() {
MonetaryCurrency currency = loanCurrency();
Money cumulativeOriginalPrincipalExpected = Money.zero(currency);
Money cumulativeOriginalInterestExpected = Money.zero(currency);
Money cumulativeOriginalTotalExpected = Money.zero(currency);
Money cumulativeTotalPaid = Money.zero(currency);
Money cumulativeTotalWaived = Money.zero(currency);
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
cumulativeOriginalPrincipalExpected = cumulativeOriginalPrincipalExpected.plus(scheduledRepayment.getPrincipal(currency));
cumulativeOriginalInterestExpected = cumulativeOriginalInterestExpected.plus(scheduledRepayment.getInterest(currency));
cumulativeTotalPaid = cumulativeTotalPaid.plus(scheduledRepayment.getPrincipalCompleted(currency).plus(
scheduledRepayment.getInterestCompleted(currency)));
cumulativeTotalWaived = cumulativeTotalWaived.plus(scheduledRepayment.getInterestWaived(currency));
}
cumulativeOriginalTotalExpected = cumulativeOriginalPrincipalExpected.plus(cumulativeOriginalInterestExpected);
Money totalOutstanding = cumulativeOriginalTotalExpected.minus(cumulativeTotalPaid.plus(cumulativeTotalWaived));
boolean isRepaidInFull = totalOutstanding.isZero();
return isRepaidInFull;
return Money.of(loanCurrency(), this.totalOutstanding).isZero();
}
private boolean isOverPaid() {
@@ -1571,9 +1811,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
cumulativeTotalPaidOnInstallments = cumulativeTotalPaidOnInstallments
.plus(scheduledRepayment.getPrincipalCompleted(currency).plus(scheduledRepayment.getInterestCompleted(currency)))
.plus(scheduledRepayment.getFeeChargesCompleted(currency))
.plus(scheduledRepayment.getPenaltyChargesCompleted(currency));
.plus(scheduledRepayment.getPrincipalCompleted(currency).plus(scheduledRepayment.getInterestPaid(currency)))
.plus(scheduledRepayment.getFeeChargesPaid(currency)).plus(scheduledRepayment.getPenaltyChargesPaid(currency));
cumulativeTotalWaivedOnInstallments = cumulativeTotalWaivedOnInstallments.plus(scheduledRepayment.getInterestWaived(currency));
}
@@ -1630,9 +1869,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanTransactions.add(loanTransaction);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), this.repaymentScheduleInstallments);
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
}
return loanTransaction;
@@ -1663,11 +1904,15 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
LoanTransaction loanTransaction = null;
if (isOpen()) {
// 1. check total outstanding
final Money outstanding = getTotalOutstanding();
final Money outstanding = Money.of(loanCurrency(), this.totalOutstanding);
if (outstanding.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(outstanding)) {
updateLoanForClosure(closureDate, loanLifecycleStateMachine, changes);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
changes.put("status", LoanEnumerations.status(this.loanStatus));
}
this.closedOnDate = closureDate.toDate();
loanTransaction = LoanTransaction.writeoff(this, closureDate);
boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, loanTransactions);
@@ -1679,11 +1924,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanTransactions.add(loanTransaction);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(),
this.repaymentScheduleInstallments);
updateLoanSummaryDerivedFields(this.loanSummaryWrapper);
} else if (outstanding.isGreaterThanZero()) {
final String errorMessage = "A loan with money outstanding cannot be closed";
throw new InvalidLoanStateTransitionException("close", "loan.has.money.outstanding", errorMessage, outstanding.toString());
@@ -1696,7 +1942,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
// TODO - KW - technically should set somewhere that this loan
// has
// 'overpaid' amount
updateLoanForClosure(closureDate, loanLifecycleStateMachine, changes);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
changes.put("status", LoanEnumerations.status(this.loanStatus));
}
this.closedOnDate = closureDate.toDate();
} else if (totalLoanOverpayment.isGreaterThanZero()) {
final String errorMessage = "The loan is marked as 'Overpaid' and cannot be moved to 'Closed (obligations met).";
throw new InvalidLoanStateTransitionException("close", "loan.is.overpaid", errorMessage, totalLoanOverpayment.toString());
@@ -1706,26 +1957,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return loanTransaction;
}
private Money getTotalOutstanding() {
Money totalOutstanding = Money.zero(loanCurrency());
final LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
totalOutstanding = wrapper.calculateTotalOutstanding(loanCurrency(), this.repaymentScheduleInstallments);
return totalOutstanding;
}
private void updateLoanForClosure(final LocalDate closureDate, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes) {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue();
changes.put("status", LoanEnumerations.status(this.loanStatus));
}
this.closedOnDate = closureDate.toDate();
}
/**
* Behaviour added to comply with capability of previous mifos product to
* support easier transition to mifosx platform.
@@ -1773,73 +2004,34 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return status().isSubmittedAndPendingApproval();
}
public boolean isApproved() {
return status().isApproved();
}
public boolean isNotApproved() {
return !isApproved();
}
public boolean isWaitingForDisbursal() {
return this.isApproved() && this.isNotDisbursed();
}
public boolean isNotDisbursed() {
private boolean isNotDisbursed() {
return !this.isDisbursed();
}
public boolean isDisbursed() {
private boolean isDisbursed() {
return hasDisbursementTransaction();
}
public boolean isUndoDisbursalAllowed() {
return isDisbursed() && this.hasNoRepaymentTransaction();
}
public boolean isClosed() {
private boolean isClosed() {
return status().isClosed() || this.isCancelled();
}
public boolean isCancelled() {
private boolean isCancelled() {
return this.isRejected() || this.isWithdrawn();
}
public boolean isWithdrawn() {
private boolean isWithdrawn() {
return status().isWithdrawnByClient();
}
public boolean isRejected() {
private boolean isRejected() {
return status().isRejected();
}
public boolean isNotClosed() {
return !this.isClosed();
}
public boolean isOpen() {
private boolean isOpen() {
return status().isActive();
}
public boolean isOpenWithNoRepaymentMade() {
return this.isOpen() && hasNoRepaymentTransaction();
}
private boolean hasNoRepaymentTransaction() {
return !hasRepaymentTransaction();
}
private boolean hasRepaymentTransaction() {
boolean hasRepaymentTransaction = false;
for (LoanTransaction loanTransaction : this.loanTransactions) {
if (loanTransaction.isRepayment()) {
hasRepaymentTransaction = true;
break;
}
}
return hasRepaymentTransaction;
}
private boolean hasDisbursementTransaction() {
boolean hasRepaymentTransaction = false;
for (LoanTransaction loanTransaction : this.loanTransactions) {
@@ -1851,35 +2043,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return hasRepaymentTransaction;
}
public boolean isOpenWithRepaymentMade() {
return this.isOpen() && this.hasRepaymentTransaction();
}
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() {
return this.repaymentScheduleInstallments;
}
public String getExternalId() {
return this.externalId;
}
public void setExternalId(final String externalSystemIdentifer) {
if (StringUtils.isNotBlank(externalSystemIdentifer)) {
this.externalId = externalSystemIdentifer.trim();
} else {
this.externalId = null;
}
}
public LocalDate getSubmittedOnDate() {
private LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
public LocalDate getRejectedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.rejectedOnDate), null);
}
public LocalDate getApprovedOnDate() {
private LocalDate getApprovedOnDate() {
LocalDate date = null;
if (this.approvedOnDate != null) {
date = new LocalDate(this.approvedOnDate);
@@ -1887,35 +2055,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return date;
}
public LocalDate getDisbursedOnDate() {
LocalDate date = null;
if (this.actualDisbursementDate != null) {
date = new LocalDate(this.actualDisbursementDate);
}
return date;
}
public LocalDate getClosedOnDate() {
LocalDate date = null;
if (this.closedOnDate != null) {
date = new LocalDate(this.closedOnDate);
}
return date;
}
public LocalDate getWrittenOffOnDate() {
LocalDate date = null;
if (this.writtenOffOnDate != null) {
date = new LocalDate(this.writtenOffOnDate);
}
return date;
}
public Date getExpectedDisbursedOnDate() {
return this.expectedDisbursementDate;
}
public LocalDate getExpectedDisbursedOnLocalDate() {
private LocalDate getExpectedDisbursedOnLocalDate() {
LocalDate expectedDisbursementDate = null;
if (this.expectedDisbursementDate != null) {
expectedDisbursementDate = new LocalDate(this.expectedDisbursementDate);
@@ -1923,15 +2063,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return expectedDisbursementDate;
}
public LocalDate getExpectedFirstRepaymentOnDate() {
LocalDate firstRepaymentDate = null;
if (this.expectedFirstRepaymentOnDate != null) {
firstRepaymentDate = new LocalDate(this.expectedFirstRepaymentOnDate);
}
return firstRepaymentDate;
}
public LocalDate getDisbursementDate() {
private LocalDate getDisbursementDate() {
LocalDate disbursementDate = getExpectedDisbursedOnLocalDate();
if (this.actualDisbursementDate != null) {
disbursementDate = new LocalDate(this.actualDisbursementDate);
@@ -1939,52 +2071,24 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return disbursementDate;
}
public LocalDate getExpectedMaturityDate() {
LocalDate possibleMaturityDate = null;
if (this.expectedMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.expectedMaturityDate);
private LocalDate getExpectedFirstRepaymentOnDate() {
LocalDate firstRepaymentDate = null;
if (this.expectedFirstRepaymentOnDate != null) {
firstRepaymentDate = new LocalDate(this.expectedFirstRepaymentOnDate);
}
return possibleMaturityDate;
return firstRepaymentDate;
}
public LocalDate getActualMaturityDate() {
LocalDate possibleMaturityDate = null;
if (this.actualMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.actualMaturityDate);
}
return possibleMaturityDate;
}
public LocalDate getMaturityDate() {
LocalDate possibleMaturityDate = null;
if (this.expectedMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.expectedMaturityDate);
}
if (this.actualMaturityDate != null) {
possibleMaturityDate = new LocalDate(this.actualMaturityDate);
}
return possibleMaturityDate;
}
public Set<LoanCharge> getCharges() {
return charges;
}
public void setCharges(final Set<LoanCharge> charges) {
this.charges = charges;
}
public void addRepaymentScheduleInstallment(final LoanRepaymentScheduleInstallment installment) {
private void addRepaymentScheduleInstallment(final LoanRepaymentScheduleInstallment installment) {
installment.updateLoan(this);
this.repaymentScheduleInstallments.add(installment);
}
public boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
private boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
return !new LocalDate(this.expectedDisbursementDate).isEqual(actualDisbursedOnDate);
}
public boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
private boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
return isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
}
@@ -2029,10 +2133,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return this.loanRepaymentScheduleDetail.getInArrearsTolerance();
}
public boolean identifiedBy(final String identifier) {
return identifier.equalsIgnoreCase(this.externalId) || identifier.equalsIgnoreCase(this.getId().toString());
}
public boolean hasIdentifyOf(final Long loanId) {
return loanId.equals(this.getId());
}
@@ -2049,7 +2149,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return matchesCurrentLoanOfficer;
}
public LocalDate getInterestChargedFromDate() {
private LocalDate getInterestChargedFromDate() {
LocalDate interestChargedFrom = null;
if (this.interestChargedFromDate != null) {
interestChargedFrom = new LocalDate(this.interestChargedFromDate);
@@ -2057,29 +2157,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return interestChargedFrom;
}
public LocalDate getLoanStatusSinceDate() {
LocalDate statusSinceDate = getSubmittedOnDate();
if (isApproved()) {
statusSinceDate = new LocalDate(this.approvedOnDate);
}
if (isDisbursed()) {
statusSinceDate = new LocalDate(this.actualDisbursementDate);
}
if (isClosed()) {
statusSinceDate = new LocalDate(this.closedOnDate);
}
return statusSinceDate;
}
public Money getPrincpal() {
return this.loanRepaymentScheduleDetail.getPrincipal();
}
public Money getTotalChargesDueAtDisbursement() {
private Money getTotalChargesDueAtDisbursement() {
return Money.of(getCurrency(), this.totalChargesDueAtDisbursement);
}
@@ -2155,10 +2237,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
return latestRecordWithNoEndDate;
}
public Client getClient() {
return client;
}
public Long getClientId() {
Long clientId = null;
if (this.client != null) {
@@ -2223,4 +2301,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
accountingBridgeData.put("newLoanTransactions", newLoanTransactions);
return accountingBridgeData;
}
public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory) {
this.loanLifecycleStateMachine = loanLifecycleStateMachine;
this.loanSummaryWrapper = loanSummaryWrapper;
this.transactionProcessorFactory = transactionProcessorFactory;
}
}
@@ -51,10 +51,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
private BigDecimal principalWrittenOff;
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal interest;
private BigDecimal interestCharged;
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestCompleted;
private BigDecimal interestPaid;
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestWaived;
@@ -63,10 +63,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
private BigDecimal interestWrittenOff;
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal feeCharges;
private BigDecimal feeChargesCharged;
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesCompleted;
private BigDecimal feeChargesPaid;
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesWrittenOff;
@@ -78,7 +78,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
private BigDecimal penaltyCharges;
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesCompleted;
private BigDecimal penaltyChargesPaid;
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal penaltyChargesWrittenOff;
@@ -104,8 +104,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
this.fromDate = fromDate.toDateMidnight().toDate();
this.dueDate = dueDate.toDateMidnight().toDate();
this.principal = defaultToNullIfZero(principal);
this.interest = defaultToNullIfZero(interest);
this.feeCharges = defaultToNullIfZero(feeCharges);
this.interestCharged = defaultToNullIfZero(interest);
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
this.completed = false;
}
@@ -156,12 +156,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
return getPrincipal(currency).minus(principalAccountedFor);
}
public Money getInterest(final MonetaryCurrency currency) {
return Money.of(currency, this.interest);
public Money getInterestCharged(final MonetaryCurrency currency) {
return Money.of(currency, this.interestCharged);
}
public Money getInterestCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.interestCompleted);
public Money getInterestPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.interestPaid);
}
public Money getInterestWaived(final MonetaryCurrency currency) {
@@ -173,17 +173,17 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public Money getInterestOutstanding(final MonetaryCurrency currency) {
final Money interestAccountedFor = getInterestCompleted(currency).plus(getInterestWaived(currency)).plus(
final Money interestAccountedFor = getInterestPaid(currency).plus(getInterestWaived(currency)).plus(
getInterestWrittenOff(currency));
return getInterest(currency).minus(interestAccountedFor);
return getInterestCharged(currency).minus(interestAccountedFor);
}
public Money getFeeCharges(final MonetaryCurrency currency) {
return Money.of(currency, this.feeCharges);
public Money getFeeChargesCharged(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesCharged);
}
public Money getFeeChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesCompleted);
public Money getFeeChargesPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.feeChargesPaid);
}
public Money getFeeChargesWaived(final MonetaryCurrency currency) {
@@ -195,17 +195,17 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public Money getFeeChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getFeeChargesCompleted(currency).plus(getFeeChargesWaived(currency)).plus(
final Money feeChargesAccountedFor = getFeeChargesPaid(currency).plus(getFeeChargesWaived(currency)).plus(
getFeeChargesWrittenOff(currency));
return getFeeCharges(currency).minus(feeChargesAccountedFor);
return getFeeChargesCharged(currency).minus(feeChargesAccountedFor);
}
public Money getPenaltyCharges(final MonetaryCurrency currency) {
public Money getPenaltyChargesCharged(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyCharges);
}
public Money getPenaltyChargesCompleted(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesCompleted);
public Money getPenaltyChargesPaid(final MonetaryCurrency currency) {
return Money.of(currency, this.penaltyChargesPaid);
}
public Money getPenaltyChargesWaived(final MonetaryCurrency currency) {
@@ -217,9 +217,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public Money getPenaltyChargesOutstanding(final MonetaryCurrency currency) {
final Money feeChargesAccountedFor = getPenaltyChargesCompleted(currency).plus(getPenaltyChargesWaived(currency)).plus(
final Money feeChargesAccountedFor = getPenaltyChargesPaid(currency).plus(getPenaltyChargesWaived(currency)).plus(
getPenaltyChargesWrittenOff(currency));
return getPenaltyCharges(currency).minus(feeChargesAccountedFor);
return getPenaltyChargesCharged(currency).minus(feeChargesAccountedFor);
}
public boolean isInterestDue(final MonetaryCurrency currency) {
@@ -227,7 +227,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public Money getTotalPrincipalAndInterest(final MonetaryCurrency currency) {
return getPrincipal(currency).plus(getInterest(currency));
return getPrincipal(currency).plus(getInterestCharged(currency));
}
public Money getTotalOutstanding(final MonetaryCurrency currency) {
@@ -258,13 +258,13 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public void resetDerivedComponents() {
this.principalCompleted = null;
this.principalWrittenOff = null;
this.interestCompleted = null;
this.interestPaid = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.feeChargesCompleted = null;
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.penaltyChargesCompleted = null;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
@@ -278,14 +278,14 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
final Money penaltyChargesDue = getPenaltyChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(penaltyChargesDue)) {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(penaltyChargesDue).getAmount();
this.penaltyChargesPaid = getPenaltyChargesPaid(currency).plus(penaltyChargesDue).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(penaltyChargesDue);
} else {
this.penaltyChargesCompleted = getPenaltyChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
this.penaltyChargesPaid = getPenaltyChargesPaid(currency).plus(transactionAmountRemaining).getAmount();
penaltyPortionOfTransaction = penaltyPortionOfTransaction.plus(transactionAmountRemaining);
}
this.penaltyChargesCompleted = defaultToNullIfZero(this.penaltyChargesCompleted);
this.penaltyChargesPaid = defaultToNullIfZero(this.penaltyChargesPaid);
this.completed = getTotalOutstanding(currency).isZero();
@@ -299,14 +299,14 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
final Money feeChargesDue = getFeeChargesOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(feeChargesDue)) {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(feeChargesDue).getAmount();
this.feeChargesPaid = getFeeChargesPaid(currency).plus(feeChargesDue).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(feeChargesDue);
} else {
this.feeChargesCompleted = getFeeChargesCompleted(currency).plus(transactionAmountRemaining).getAmount();
this.feeChargesPaid = getFeeChargesPaid(currency).plus(transactionAmountRemaining).getAmount();
feePortionOfTransaction = feePortionOfTransaction.plus(transactionAmountRemaining);
}
this.feeChargesCompleted = defaultToNullIfZero(this.feeChargesCompleted);
this.feeChargesPaid = defaultToNullIfZero(this.feeChargesPaid);
this.completed = getTotalOutstanding(currency).isZero();
@@ -320,14 +320,14 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
final Money interestDue = getInterestOutstanding(currency);
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
this.interestCompleted = getInterestCompleted(currency).plus(interestDue).getAmount();
this.interestPaid = getInterestPaid(currency).plus(interestDue).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(interestDue);
} else {
this.interestCompleted = getInterestCompleted(currency).plus(transactionAmountRemaining).getAmount();
this.interestPaid = getInterestPaid(currency).plus(transactionAmountRemaining).getAmount();
interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestCompleted = defaultToNullIfZero(this.interestCompleted);
this.interestPaid = defaultToNullIfZero(this.interestPaid);
this.completed = getTotalOutstanding(currency).isZero();
@@ -455,7 +455,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public void updateChargePortion(final Money feeChargesDue, final Money feeChargesWaived, final Money feeChargesWrittenOff,
final Money penaltyChargesDue, final Money penaltyChargesWaived, final Money penaltyChargesWrittenOff) {
this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount());
this.feeChargesCharged = defaultToNullIfZero(feeChargesDue.getAmount());
this.feeChargesWaived = defaultToNullIfZero(feeChargesWaived.getAmount());
this.feeChargesWrittenOff = defaultToNullIfZero(feeChargesWrittenOff.getAmount());
this.penaltyCharges = defaultToNullIfZero(penaltyChargesDue.getAmount());
@@ -15,27 +15,9 @@ import org.mifosplatform.organisation.monetary.domain.Money;
/**
* A wrapper around loan schedule related data exposing needed behaviour by
* loan.
*
* This wrapper should be side-effect free. It should not attempt to change
* state of loan parts passed through to it.
*/
public class LoanScheduleWrapper {
public class LoanRepaymentScheduleProcessingWrapper {
public Money calculateTotalOutstanding(final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments) {
Money cumulativeValue = Money.zero(currency);
for (LoanRepaymentScheduleInstallment scheduledRepayment : installments) {
cumulativeValue = cumulativeValue.plus(scheduledRepayment.getTotalOutstanding(currency));
}
return cumulativeValue;
}
/**
* FIXME - KW - this is not a side effect free approach so move this into a
* specific wrapper than makes this obvious from the read-only wrapper.
*/
public void reprocess(final MonetaryCurrency currency, final LocalDate disbursementDate,
final List<LoanRepaymentScheduleInstallment> repaymentPeriods, final Set<LoanCharge> loanCharges) {
@@ -6,36 +6,35 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.springframework.stereotype.Component;
/**
*
*/
@Component
public class LoanRepaymentScheduleTransactionProcessorFactory {
public LoanRepaymentScheduleTransactionProcessor determineProcessor(final LoanTransactionProcessingStrategy transactionProcessingStrategy) {
public LoanRepaymentScheduleTransactionProcessor determineProcessor(
final LoanTransactionProcessingStrategy transactionProcessingStrategy) {
LoanRepaymentScheduleTransactionProcessor processor = new MifosStyleLoanRepaymentScheduleTransactionProcessor();
if (transactionProcessingStrategy != null) {
if (transactionProcessingStrategy.isStandardMifosStrategy()) {
processor = new MifosStyleLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isHeavensfamilyStrategy()) {
processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isCreocoreStrategy()) {
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
}
return processor;
}
LoanRepaymentScheduleTransactionProcessor processor = new MifosStyleLoanRepaymentScheduleTransactionProcessor();
if (transactionProcessingStrategy != null) {
if (transactionProcessingStrategy.isStandardMifosStrategy()) {
processor = new MifosStyleLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isHeavensfamilyStrategy()) {
processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isCreocoreStrategy()) {
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
}
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
processor = new RBILoanRepaymentScheduleTransactionProcessor();
}
}
return processor;
}
}
@@ -0,0 +1,215 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.springframework.stereotype.Component;
/**
* A wrapper for dealing with side-effect free functionality related to a loans
* transactions and repayment schedule.
*/
@Component
public final class LoanSummaryWrapper {
public Money calculateTotalPrincipalRepaid(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPrincipalCompleted(currency));
}
return total;
}
public Money calculateTotalPrincipalWrittenOff(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPrincipalWrittenOff(currency));
}
return total;
}
public Money calculateTotalPrincipalOverdueOn(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate overdueAsOf) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(overdueAsOf)) {
total = total.plus(installment.getPrincipalOutstanding(currency));
}
}
return total;
}
public Money calculateTotalInterestCharged(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getInterestCharged(currency));
}
return total;
}
public Money calculateTotalInterestRepaid(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getInterestPaid(currency));
}
return total;
}
public Money calculateTotalInterestWaived(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getInterestWaived(currency));
}
return total;
}
public Money calculateTotalInterestWrittenOff(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getInterestWrittenOff(currency));
}
return total;
}
public Money calculateTotalInterestOverdueOn(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate overdueAsOf) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(overdueAsOf)) {
total = total.plus(installment.getInterestOutstanding(currency));
}
}
return total;
}
public Money calculateTotalFeeChargesCharged(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getFeeChargesCharged(currency));
}
return total;
}
public Money calculateTotalFeeChargesRepaid(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getFeeChargesPaid(currency));
}
return total;
}
public Money calculateTotalFeeChargesWaived(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getFeeChargesWaived(currency));
}
return total;
}
public Money calculateTotalFeeChargesWrittenOff(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getFeeChargesWrittenOff(currency));
}
return total;
}
public Money calculateTotalFeeChargesOverdueOn(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate overdueAsOf) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(overdueAsOf)) {
total = total.plus(installment.getFeeChargesOutstanding(currency));
}
}
return total;
}
public Money calculateTotalPenaltyChargesCharged(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPenaltyChargesCharged(currency));
}
return total;
}
public Money calculateTotalPenaltyChargesRepaid(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPenaltyChargesPaid(currency));
}
return total;
}
public Money calculateTotalPenaltyChargesWaived(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPenaltyChargesWaived(currency));
}
return total;
}
public Money calculateTotalPenaltyChargesWrittenOff(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
total = total.plus(installment.getPenaltyChargesWrittenOff(currency));
}
return total;
}
public Money calculateTotalPenaltyChargesOverdueOn(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate overdueAsOf) {
Money total = Money.zero(currency);
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(overdueAsOf)) {
total = total.plus(installment.getPenaltyChargesOutstanding(currency));
}
}
return total;
}
public Money calculateTotalOverdueOn(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate overdueAsOf) {
final Money principalOverdue = calculateTotalPrincipalOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
final Money interestOverdue = calculateTotalInterestOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
final Money feeChargesOverdue = calculateTotalFeeChargesOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
final Money penaltyChargesOverdue = calculateTotalPenaltyChargesOverdueOn(repaymentScheduleInstallments, currency, overdueAsOf);
return principalOverdue.plus(interestOverdue).plus(feeChargesOverdue).plus(penaltyChargesOverdue);
}
public LocalDate determineOverdueSinceDateFrom(final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments,
final MonetaryCurrency currency, final LocalDate from) {
LocalDate overdueSince = null;
final Money totalOverdue = calculateTotalOverdueOn(repaymentScheduleInstallments, currency, from);
if (totalOverdue.isGreaterThanZero()) {
for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(from)) {
if (overdueSince == null || overdueSince.isAfter(installment.getDueDate())) {
overdueSince = installment.getDueDate();
}
}
}
}
return overdueSince;
}
}
@@ -14,131 +14,32 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
* Immutable data object to represent aspects of a loan schedule such as:
*
* <ul>
* <li>summary information - the totals for each part of schedule monitored</li>
* <li>repayment schedule - the principal due, outstanding balance and cost of
* loan items such as interest and charges</li>
* <li>Totals information - the totals for each part of repayment schedule
* monitored.</li>
* <li>Repayment schedule - the principal due, outstanding balance and cost of
* loan items such as interest and charges (both fees and penalties)</li>
* </ul>
*/
@SuppressWarnings("unused")
public class LoanScheduleData {
@SuppressWarnings("unused")
private final Integer loanTermInDays;
/**
* The currency associated with all monetary values in loan schedule.
*/
@SuppressWarnings("unused")
private final CurrencyData currency;
/**
* The cumulative total of all money (principal) disbursed to the loan
* applicant.
*/
@SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalDisbursed;
/**
* The cumulative total of all <code>principalDue</code> for each period of
* loan schedule. (originalPrincipal)
*/
@SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalDue;
@SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalPaid;
@SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalWrittenOff;
@SuppressWarnings("unused")
private final BigDecimal cumulativePrincipalOutstanding;
/**
* The cumulative total of interest expected on any principal disbursed.
* (originalInterest)
*/
@SuppressWarnings("unused")
private final BigDecimal cumulativeInterestExpected;
@SuppressWarnings("unused")
private final BigDecimal cumulativeInterestPaid;
@SuppressWarnings("unused")
private final BigDecimal cumulativeInterestWaived;
@SuppressWarnings("unused")
private final BigDecimal cumulativeInterestWrittenOff;
@SuppressWarnings("unused")
private final BigDecimal cumulativeInterestOutstanding;
/**
* The cumulative total of all fee charges applied on the loan to date.
*/
@SuppressWarnings("unused")
private final BigDecimal cumulativeFeeChargesToDate;
@SuppressWarnings("unused")
private final BigDecimal cumulativeFeeChargesPaid;
@SuppressWarnings("unused")
private final BigDecimal cumulativeFeeChargesWaived;
@SuppressWarnings("unused")
private final BigDecimal cumulativeFeeChargesWrittenOff;
@SuppressWarnings("unused")
private final BigDecimal cumulativeFeeChargesOutstanding;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesToDate;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesPaid;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesWaived;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesWrittenOff;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesOutstanding;
/**
* The cumulative total of all costs on the loan. Costs tracked are:
* <ul>
* <li>Interest</li>
* <li>Charges (fees and penalties)</li>
* </ul>
*/
@SuppressWarnings("unused")
private final BigDecimal totalCostOfLoan;
/**
* The sum of <code>principalDisbursed</code> and
* <code>totalCostOfLoan</code>. (originalTotal)
*/
@SuppressWarnings("unused")
private final BigDecimal totalExpectedRepayment;
/**
* The cumulative sum of all repayments to date. (totalPaid)
*/
@SuppressWarnings("unused")
private final BigDecimal totalPaidToDate;
/**
* The cumulative sum of all waivers to date.
*/
@SuppressWarnings("unused")
private final BigDecimal totalWaivedToDate;
/**
* The cumulative sum of all waivers to date.
*/
@SuppressWarnings("unused")
private final BigDecimal totalWrittenOffToDate;
/**
* The cumulative sum of all principal, interest & charges outstanding.
*/
private final Integer loanTermInDays;
private final BigDecimal totalPrincipalDisbursed;
private final BigDecimal totalPrincipalExpected;
private final BigDecimal totalPrincipalPaid;
private final BigDecimal totalInterestCharged;
private final BigDecimal totalFeeChargesCharged;
private final BigDecimal totalPenaltyChargesCharged;
private final BigDecimal totalWaived;
private final BigDecimal totalWrittenOff;
private final BigDecimal totalRepaymentExpected;
private final BigDecimal totalRepayment;
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.
*/
@SuppressWarnings("unused")
private final BigDecimal totalInArrears;
/**
* <code>periods</code> is collection of data objects containing specific
* information to each period of the loan schedule including disbursement
@@ -146,148 +47,28 @@ public class LoanScheduleData {
*/
private final Collection<LoanSchedulePeriodData> periods;
public LoanScheduleData() {
this.cumulativePrincipalDisbursed = null;
this.cumulativePrincipalPaid = null;
this.cumulativePrincipalWrittenOff = null;
this.cumulativePrincipalDue = null;
this.cumulativePrincipalOutstanding = null;
this.cumulativeInterestExpected = null;
this.cumulativeInterestPaid = null;
this.cumulativeInterestWaived = null;
this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeFeeChargesToDate = null;
this.cumulativeFeeChargesPaid = null;
this.cumulativeFeeChargesWaived = null;
this.cumulativeFeeChargesWrittenOff = null;
this.cumulativeFeeChargesOutstanding = null;
this.cumulativePenaltyChargesToDate = null;
this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesWaived = null;
this.cumulativePenaltyChargesWrittenOff = null;
this.cumulativePenaltyChargesOutstanding = null;
this.totalCostOfLoan = null;
this.totalExpectedRepayment = null;
this.totalPaidToDate = null;
this.totalWaivedToDate = null;
this.totalWrittenOffToDate = null;
this.totalOutstanding = null;
this.totalInArrears = null;
this.currency = null;
this.periods = null;
this.loanTermInDays = null;
}
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
final BigDecimal cumulativePrincipalDisbursed, final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalOutstanding, final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeFeeChargesToDate, final BigDecimal totalExpectedRepayment) {
final BigDecimal totalPrincipalDisbursed, final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid,
final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged,
final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalRepaymentExpected,
final BigDecimal totalRepayment, final BigDecimal totalOutstanding) {
this.currency = currency;
this.periods = periods;
this.loanTermInDays = loanTermInDays;
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
this.cumulativePrincipalDue = cumulativePrincipalDue;
this.cumulativePrincipalPaid = null;
this.cumulativePrincipalWrittenOff = null;
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
this.cumulativeInterestExpected = cumulativeInterestExpected;
this.cumulativeInterestPaid = null;
this.cumulativeInterestWaived = null;
this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeFeeChargesPaid = null;
this.cumulativeFeeChargesWaived = null;
this.cumulativeFeeChargesWrittenOff = null;
this.cumulativeFeeChargesOutstanding = null;
this.cumulativePenaltyChargesToDate = null;
this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesWaived = null;
this.cumulativePenaltyChargesWrittenOff = null;
this.cumulativePenaltyChargesOutstanding = null;
if (cumulativeFeeChargesToDate != null) {
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate);
} else if (cumulativeInterestExpected != null) {
this.totalCostOfLoan = cumulativeInterestExpected;
} else {
this.totalCostOfLoan = null;
}
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = BigDecimal.ZERO;
this.totalWaivedToDate = BigDecimal.ZERO;
this.totalWrittenOffToDate = BigDecimal.ZERO;
this.totalOutstanding = totalExpectedRepayment;
this.totalInArrears = BigDecimal.ZERO;
}
/*
* Used when fully populating
*/
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
final BigDecimal cumulativePrincipalDisbursed, final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalPaid, final BigDecimal cumulativePrincipalWrittenOff,
final BigDecimal cumulativePrincipalOutstanding, final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeInterestPaid, final BigDecimal cumulativeInterestWaived,
final BigDecimal cumulativeInterestWrittenOff, final BigDecimal cumulativeInterestOutstanding,
final BigDecimal cumulativeFeeChargesToDate, final BigDecimal cumulativeFeeChargesPaid,
final BigDecimal cumulativeFeeChargesWaived, final BigDecimal cumulativeFeeChargesWrittenOff,
final BigDecimal cumulativeFeeChargesOutstanding, final BigDecimal cumulativePenaltyChargesToDate,
final BigDecimal cumulativePenaltyChargesPaid, final BigDecimal cumulativePenaltyChargesWaived,
final BigDecimal cumulativePenaltyChargesWrittenOff, final BigDecimal cumulativePenaltyChargesOutstanding,
final BigDecimal totalCostOfLoan, final BigDecimal totalExpectedRepayment, final BigDecimal totalPaidToDate,
final BigDecimal totalWaivedToDate, final BigDecimal totalWrittenOffToDate, 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.cumulativePrincipalWrittenOff = cumulativePrincipalWrittenOff;
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
this.cumulativeInterestExpected = cumulativeInterestExpected;
this.cumulativeInterestPaid = cumulativeInterestPaid;
this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
this.cumulativeInterestWaived = cumulativeInterestWaived;
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeFeeChargesPaid = cumulativeFeeChargesPaid;
this.cumulativeFeeChargesWaived = cumulativeFeeChargesWaived;
this.cumulativeFeeChargesWrittenOff = cumulativeFeeChargesWrittenOff;
this.cumulativeFeeChargesOutstanding = cumulativeFeeChargesOutstanding;
this.cumulativePenaltyChargesToDate = cumulativePenaltyChargesToDate;
this.cumulativePenaltyChargesPaid = cumulativePenaltyChargesPaid;
this.cumulativePenaltyChargesWaived = cumulativePenaltyChargesWaived;
this.cumulativePenaltyChargesWrittenOff = cumulativePenaltyChargesWrittenOff;
this.cumulativePenaltyChargesOutstanding = cumulativePenaltyChargesOutstanding;
this.totalCostOfLoan = totalCostOfLoan;
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = totalPaidToDate;
this.totalWaivedToDate = totalWaivedToDate;
this.totalWrittenOffToDate = totalWrittenOffToDate;
this.totalPrincipalDisbursed = totalPrincipalDisbursed;
this.totalPrincipalExpected = totalPrincipalExpected;
this.totalPrincipalPaid = totalPrincipalPaid;
this.totalInterestCharged = totalInterestCharged;
this.totalFeeChargesCharged = totalFeeChargesCharged;
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
this.totalWaived = totalWaived;
this.totalWrittenOff = totalWrittenOff;
this.totalRepaymentExpected = totalRepaymentExpected;
this.totalRepayment = totalRepayment;
this.totalOutstanding = totalOutstanding;
this.totalInArrears = totalInArrears;
}
public Collection<LoanSchedulePeriodData> getPeriods() {
return periods;
}
public BigDecimal totalOutstanding() {
return this.totalOutstanding;
}
}
@@ -16,439 +16,381 @@ import org.joda.time.LocalDate;
*/
public class LoanSchedulePeriodData {
private final Integer period;
private final LocalDate fromDate;
private final LocalDate dueDate;
private final Integer daysInPeriod;
private final BigDecimal principalDisbursed;
@SuppressWarnings("unused")
private final BigDecimal principalOriginalDue;
private final BigDecimal principalDue;
private final BigDecimal principalPaid;
private final BigDecimal principalWrittenOff;
private final BigDecimal principalOutstanding;
@SuppressWarnings("unused")
private final BigDecimal principalLoanBalanceOutstanding;
@SuppressWarnings("unused")
private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding;
private final BigDecimal feeChargesDue;
private final BigDecimal feeChargesPaid;
private final BigDecimal feeChargesWaived;
private final BigDecimal feeChargesWrittenOff;
private final BigDecimal feeChargesOutstanding;
private final BigDecimal penaltyChargesDue;
private final BigDecimal penaltyChargesPaid;
private final BigDecimal penaltyChargesWaived;
private final BigDecimal penaltyChargesWrittenOff;
private final BigDecimal penaltyChargesOutstanding;
@SuppressWarnings("unused")
private final BigDecimal totalOriginalDueForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalDueForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalPaidForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalWaivedForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalWrittenOffForPeriod;
private final BigDecimal totalOutstandingForPeriod;
private final BigDecimal totalOverdue;
@SuppressWarnings("unused")
private final BigDecimal totalActualCostOfLoanForPeriod;
private final Integer period;
private final LocalDate fromDate;
private final LocalDate dueDate;
private final Integer daysInPeriod;
private final BigDecimal principalDisbursed;
@SuppressWarnings("unused")
private final BigDecimal principalOriginalDue;
private final BigDecimal principalDue;
private final BigDecimal principalPaid;
private final BigDecimal principalWrittenOff;
private final BigDecimal principalOutstanding;
@SuppressWarnings("unused")
private final BigDecimal principalLoanBalanceOutstanding;
@SuppressWarnings("unused")
private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue;
private final BigDecimal interestPaid;
private final BigDecimal interestWaived;
private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding;
private final BigDecimal feeChargesDue;
private final BigDecimal feeChargesPaid;
private final BigDecimal feeChargesWaived;
private final BigDecimal feeChargesWrittenOff;
private final BigDecimal feeChargesOutstanding;
private final BigDecimal penaltyChargesDue;
private final BigDecimal penaltyChargesPaid;
private final BigDecimal penaltyChargesWaived;
private final BigDecimal penaltyChargesWrittenOff;
private final BigDecimal penaltyChargesOutstanding;
public static LoanSchedulePeriodData disbursementOnlyPeriod(
final LocalDate disbursementDate,
final BigDecimal principalDisbursed,
final BigDecimal feeChargesDueAtTimeOfDisbursement,
final boolean isDisbursed) {
Integer periodNumber = Integer.valueOf(0);
LocalDate from = null;
return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, feeChargesDueAtTimeOfDisbursement, isDisbursed);
}
public static LoanSchedulePeriodData repaymentOnlyPeriod(
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalDue,
final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
final BigDecimal totalDueForPeriod) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding,
interestDueOnPrincipalOutstanding, feeChargesDueForPeriod, penaltyChargesDueForPeriod, totalDueForPeriod);
}
public static LoanSchedulePeriodData repaymentPeriodWithPayments(
@SuppressWarnings("unused") final Long loanId,
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalPaid,
final BigDecimal principalWrittenOff,
final BigDecimal principalOutstanding,
final BigDecimal outstandingPrincipalBalanceOfLoan,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid,
final BigDecimal interestWaived,
final BigDecimal interestWrittenOff,
final BigDecimal interestOutstanding,
final BigDecimal feeChargesDue,
final BigDecimal feeChargesPaid,
final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
penaltyChargesDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding,
totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding, totalActualCostOfLoanForPeriod);
}
/*
* constructor used for creating period on loan schedule that is only a disbursement (typically first period)
*/
private LoanSchedulePeriodData(
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalDisbursed,
final BigDecimal chargesDueAtTimeOfDisbursement,
final boolean isDisbursed) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = principalDisbursed;
this.principalOriginalDue = null;
this.principalDue = null;
this.principalPaid = null;
this.principalWrittenOff = null;
this.principalOutstanding = null;
this.principalLoanBalanceOutstanding = principalDisbursed;
this.interestOriginalDue = null;
this.interestDue = null;
this.interestPaid = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.interestOutstanding = null;
this.feeChargesDue = chargesDueAtTimeOfDisbursement;
if (isDisbursed) {
this.feeChargesPaid = chargesDueAtTimeOfDisbursement;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
} else {
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement;
}
this.penaltyChargesDue = null;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = this.feeChargesPaid;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = this.feeChargesOutstanding;
this.totalActualCostOfLoanForPeriod = this.feeChargesDue;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
private LoanSchedulePeriodData(
final Integer periodNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principalOriginalDue,
final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
final BigDecimal totalDueForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = null;
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = null;
this.principalWrittenOff = null;
this.principalOutstanding = principalOriginalDue;
this.principalLoanBalanceOutstanding = principalOutstanding;
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.feeChargesDue = feeChargesDueForPeriod;
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
this.penaltyChargesDue = penaltyChargesDueForPeriod;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod);
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
/*
* 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 principalWrittenOff,
final BigDecimal principalOutstanding,
final BigDecimal principalLoanBalanceOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid,
final BigDecimal interestWaived,
final BigDecimal interestWrittenOff,
final BigDecimal interestOutstanding,
final BigDecimal feeChargesDue,
final BigDecimal feeChargesPaid,
final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = null;
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = principalPaid;
this.principalWrittenOff = principalWrittenOff;
this.principalOutstanding = principalOutstanding;
this.principalLoanBalanceOutstanding = principalLoanBalanceOutstanding;
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = interestPaid;
this.interestWaived = interestWaived;
this.interestWrittenOff = interestWrittenOff;
this.interestOutstanding = interestOutstanding;
this.feeChargesDue = feeChargesDue;
this.feeChargesPaid = feeChargesPaid;
this.feeChargesWaived = feeChargesWaived;
this.feeChargesWrittenOff = feeChargesWrittenOff;
this.feeChargesOutstanding = feeChargesOutstanding;
this.penaltyChargesDue = penaltyChargesDue;
this.penaltyChargesPaid = penaltyChargesPaid;
this.penaltyChargesWaived = penaltyChargesWaived;
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
this.penaltyChargesOutstanding = penaltyChargesOutstanding;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = totalPaid;
this.totalWaivedForPeriod = totalWaived;
this.totalWrittenOffForPeriod = totalWrittenOff;
this.totalOutstandingForPeriod = totalOutstanding;
this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
BigDecimal value = BigDecimal.ZERO;
if (possibleNullValue != null) {
value = possibleNullValue;
}
return value;
}
@SuppressWarnings("unused")
private final BigDecimal totalOriginalDueForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalDueForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalPaidForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalWaivedForPeriod;
@SuppressWarnings("unused")
private final BigDecimal totalWrittenOffForPeriod;
private final BigDecimal totalOutstandingForPeriod;
private final BigDecimal totalOverdue;
@SuppressWarnings("unused")
private final BigDecimal totalActualCostOfLoanForPeriod;
public boolean isRepaymentPeriod() {
boolean isRepaymentPeriod = false;
if (principalDue != null && this.interestDue != null) {
isRepaymentPeriod = BigDecimal.ZERO.compareTo(this.principalDue) == -1 || BigDecimal.ZERO.compareTo(this.interestDue) == -1;
}
return isRepaymentPeriod;
}
public static LoanSchedulePeriodData disbursementOnlyPeriod(final LocalDate disbursementDate, final BigDecimal principalDisbursed,
final BigDecimal feeChargesDueAtTimeOfDisbursement, final boolean isDisbursed) {
Integer periodNumber = Integer.valueOf(0);
LocalDate from = null;
return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, feeChargesDueAtTimeOfDisbursement,
isDisbursed);
}
public Integer periodNumber() {
return this.period;
}
public static LoanSchedulePeriodData repaymentOnlyPeriod(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
final BigDecimal principalDue, final BigDecimal principalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal feeChargesDueForPeriod, final BigDecimal penaltyChargesDueForPeriod, final BigDecimal totalDueForPeriod) {
public LocalDate periodFromDate() {
return this.fromDate;
}
public LocalDate periodDueDate() {
return this.dueDate;
}
public Integer daysInPeriod() {
return this.daysInPeriod;
}
public BigDecimal principalDisbursed() {
return defaultToZeroIfNull(this.principalDisbursed);
}
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding,
interestDueOnPrincipalOutstanding, feeChargesDueForPeriod, penaltyChargesDueForPeriod, totalDueForPeriod);
}
public BigDecimal principalDue() {
return defaultToZeroIfNull(this.principalDue);
}
public static LoanSchedulePeriodData repaymentPeriodWithPayments(@SuppressWarnings("unused") final Long loanId,
final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalOriginalDue,
final BigDecimal principalPaid, final BigDecimal principalWrittenOff, final BigDecimal principalOutstanding,
final BigDecimal outstandingPrincipalBalanceOfLoan, final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
final BigDecimal interestOutstanding, final BigDecimal feeChargesDue, final BigDecimal feeChargesPaid,
final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue, final BigDecimal penaltyChargesPaid, final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding, final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff, final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
public BigDecimal principalPaid() {
return defaultToZeroIfNull(this.principalPaid);
}
public BigDecimal principalWrittenOff() {
return defaultToZeroIfNull(this.principalWrittenOff);
}
public BigDecimal principalOutstanding() {
return defaultToZeroIfNull(this.principalOutstanding);
}
public BigDecimal interestDue() {
return defaultToZeroIfNull(this.interestDue);
}
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalOriginalDue, principalPaid, principalWrittenOff,
principalOutstanding, outstandingPrincipalBalanceOfLoan, interestDueOnPrincipalOutstanding, interestPaid, interestWaived,
interestWrittenOff, interestOutstanding, feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff,
feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff,
penaltyChargesOutstanding, totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding,
totalActualCostOfLoanForPeriod);
}
public BigDecimal interestPaid() {
return defaultToZeroIfNull(this.interestPaid);
}
/*
* constructor used for creating period on loan schedule that is only a
* disbursement (typically first period)
*/
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
final BigDecimal principalDisbursed, final BigDecimal chargesDueAtTimeOfDisbursement, final boolean isDisbursed) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = principalDisbursed;
this.principalOriginalDue = null;
this.principalDue = null;
this.principalPaid = null;
this.principalWrittenOff = null;
this.principalOutstanding = null;
this.principalLoanBalanceOutstanding = principalDisbursed;
public BigDecimal interestWaived() {
return defaultToZeroIfNull(this.interestWaived);
}
public BigDecimal interestWrittenOff() {
return defaultToZeroIfNull(this.interestWrittenOff);
}
this.interestOriginalDue = null;
this.interestDue = null;
this.interestPaid = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.interestOutstanding = null;
public BigDecimal interestOutstanding() {
return defaultToZeroIfNull(this.interestOutstanding);
}
this.feeChargesDue = chargesDueAtTimeOfDisbursement;
if (isDisbursed) {
this.feeChargesPaid = chargesDueAtTimeOfDisbursement;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
} else {
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement;
}
public BigDecimal feeChargesDue() {
return defaultToZeroIfNull(this.feeChargesDue);
}
public BigDecimal feeChargesWaived() {
return defaultToZeroIfNull(this.feeChargesWaived);
}
public BigDecimal feeChargesWrittenOff() {
return defaultToZeroIfNull(this.feeChargesWrittenOff);
}
this.penaltyChargesDue = null;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
public BigDecimal feeChargesPaid() {
return defaultToZeroIfNull(this.feeChargesPaid);
}
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = this.feeChargesPaid;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = this.feeChargesOutstanding;
this.totalActualCostOfLoanForPeriod = this.feeChargesDue;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
public BigDecimal feeChargesOutstanding() {
return defaultToZeroIfNull(this.feeChargesOutstanding);
}
public BigDecimal penaltyChargesDue() {
return defaultToZeroIfNull(this.penaltyChargesDue);
}
public BigDecimal penaltyChargesWaived() {
return defaultToZeroIfNull(this.penaltyChargesWaived);
}
public BigDecimal penaltyChargesWrittenOff() {
return defaultToZeroIfNull(this.penaltyChargesWrittenOff);
}
private LoanSchedulePeriodData(final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate,
final BigDecimal principalOriginalDue, final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod, final BigDecimal totalDueForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = null;
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = null;
this.principalWrittenOff = null;
this.principalOutstanding = principalOriginalDue;
this.principalLoanBalanceOutstanding = principalOutstanding;
public BigDecimal penaltyChargesPaid() {
return defaultToZeroIfNull(this.penaltyChargesPaid);
}
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
public BigDecimal penaltyChargesOutstanding() {
return defaultToZeroIfNull(this.penaltyChargesOutstanding);
}
this.feeChargesDue = feeChargesDueForPeriod;
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
public BigDecimal totalOverdue() {
return defaultToZeroIfNull(this.totalOverdue);
}
this.penaltyChargesDue = penaltyChargesDueForPeriod;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod);
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
/*
* 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 principalWrittenOff,
final BigDecimal principalOutstanding, final BigDecimal principalLoanBalanceOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal interestPaid, final BigDecimal interestWaived,
final BigDecimal interestWrittenOff, final BigDecimal interestOutstanding, final BigDecimal feeChargesDue,
final BigDecimal feeChargesPaid, final BigDecimal feeChargesWaived, final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding, final BigDecimal penaltyChargesDue, final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived, final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod, final BigDecimal totalPaid, final BigDecimal totalWaived, final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding, final BigDecimal totalActualCostOfLoanForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
if (fromDate != null) {
this.daysInPeriod = Days.daysBetween(this.fromDate, this.dueDate).getDays();
} else {
this.daysInPeriod = null;
}
this.principalDisbursed = null;
this.principalOriginalDue = principalOriginalDue;
this.principalDue = principalOriginalDue;
this.principalPaid = principalPaid;
this.principalWrittenOff = principalWrittenOff;
this.principalOutstanding = principalOutstanding;
this.principalLoanBalanceOutstanding = principalLoanBalanceOutstanding;
this.interestOriginalDue = interestDueOnPrincipalOutstanding;
this.interestDue = interestDueOnPrincipalOutstanding;
this.interestPaid = interestPaid;
this.interestWaived = interestWaived;
this.interestWrittenOff = interestWrittenOff;
this.interestOutstanding = interestOutstanding;
this.feeChargesDue = feeChargesDue;
this.feeChargesPaid = feeChargesPaid;
this.feeChargesWaived = feeChargesWaived;
this.feeChargesWrittenOff = feeChargesWrittenOff;
this.feeChargesOutstanding = feeChargesOutstanding;
this.penaltyChargesDue = penaltyChargesDue;
this.penaltyChargesPaid = penaltyChargesPaid;
this.penaltyChargesWaived = penaltyChargesWaived;
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
this.penaltyChargesOutstanding = penaltyChargesOutstanding;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = totalPaid;
this.totalWaivedForPeriod = totalWaived;
this.totalWrittenOffForPeriod = totalWrittenOff;
this.totalOutstandingForPeriod = totalOutstanding;
this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
}
private BigDecimal defaultToZeroIfNull(final BigDecimal possibleNullValue) {
BigDecimal value = BigDecimal.ZERO;
if (possibleNullValue != null) {
value = possibleNullValue;
}
return value;
}
public boolean isRepaymentPeriod() {
boolean isRepaymentPeriod = false;
if (principalDue != null && this.interestDue != null) {
isRepaymentPeriod = BigDecimal.ZERO.compareTo(this.principalDue) == -1 || BigDecimal.ZERO.compareTo(this.interestDue) == -1;
}
return isRepaymentPeriod;
}
public Integer periodNumber() {
return this.period;
}
public LocalDate periodFromDate() {
return this.fromDate;
}
public LocalDate periodDueDate() {
return this.dueDate;
}
public Integer daysInPeriod() {
return this.daysInPeriod;
}
public BigDecimal principalDisbursed() {
return defaultToZeroIfNull(this.principalDisbursed);
}
public BigDecimal principalDue() {
return defaultToZeroIfNull(this.principalDue);
}
public BigDecimal principalPaid() {
return defaultToZeroIfNull(this.principalPaid);
}
public BigDecimal principalWrittenOff() {
return defaultToZeroIfNull(this.principalWrittenOff);
}
public BigDecimal principalOutstanding() {
return defaultToZeroIfNull(this.principalOutstanding);
}
public BigDecimal interestDue() {
return defaultToZeroIfNull(this.interestDue);
}
public BigDecimal interestPaid() {
return defaultToZeroIfNull(this.interestPaid);
}
public BigDecimal interestWaived() {
return defaultToZeroIfNull(this.interestWaived);
}
public BigDecimal interestWrittenOff() {
return defaultToZeroIfNull(this.interestWrittenOff);
}
public BigDecimal interestOutstanding() {
return defaultToZeroIfNull(this.interestOutstanding);
}
public BigDecimal feeChargesDue() {
return defaultToZeroIfNull(this.feeChargesDue);
}
public BigDecimal feeChargesWaived() {
return defaultToZeroIfNull(this.feeChargesWaived);
}
public BigDecimal feeChargesWrittenOff() {
return defaultToZeroIfNull(this.feeChargesWrittenOff);
}
public BigDecimal feeChargesPaid() {
return defaultToZeroIfNull(this.feeChargesPaid);
}
public BigDecimal feeChargesOutstanding() {
return defaultToZeroIfNull(this.feeChargesOutstanding);
}
public BigDecimal penaltyChargesDue() {
return defaultToZeroIfNull(this.penaltyChargesDue);
}
public BigDecimal penaltyChargesWaived() {
return defaultToZeroIfNull(this.penaltyChargesWaived);
}
public BigDecimal penaltyChargesWrittenOff() {
return defaultToZeroIfNull(this.penaltyChargesWrittenOff);
}
public BigDecimal penaltyChargesPaid() {
return defaultToZeroIfNull(this.penaltyChargesPaid);
}
public BigDecimal penaltyChargesOutstanding() {
return defaultToZeroIfNull(this.penaltyChargesOutstanding);
}
public BigDecimal totalOverdue() {
return defaultToZeroIfNull(this.totalOverdue);
}
}
@@ -70,10 +70,17 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0);
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount();
BigDecimal totalPrincipalExpected = BigDecimal.ZERO;
BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
BigDecimal totalInterestCharged = BigDecimal.ZERO;
BigDecimal totalFeeChargesCharged = BigDecimal.ZERO;
BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO;
BigDecimal totalWaived = BigDecimal.ZERO;
BigDecimal totalWrittenOff = BigDecimal.ZERO;
BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
BigDecimal totalRepayment = BigDecimal.ZERO;
BigDecimal totalOutstanding = BigDecimal.ZERO;
LocalDate startDate = disbursementDate;
int periodNumber = 1;
@@ -138,21 +145,20 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
// handle cumulative fields
loanTermInDays += daysInPeriod;
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
totalPrincipalExpected = totalPrincipalExpected.add(principalForInstallment.getAmount());
totalInterestCharged = totalInterestCharged.add(interestForInstallment.getAmount());
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
startDate = scheduledDueDate;
periodNumber++;
}
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(),
monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(),
currency.getDisplaySymbol(), currency.getNameCode());
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected,
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff,
totalRepaymentExpected, totalRepayment, totalOutstanding);
}
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
@@ -24,123 +24,125 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
public class EqualPrincipalAmortizationLoanScheduleGenerator implements AmortizationLoanScheduleGenerator {
private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
@Override
public LoanScheduleData generate(
final ApplicationCurrency currency,
final LoanProductRelatedDetail loanScheduleInfo,
final LocalDate disbursementDate,
final LocalDate interestCalculatedFrom,
final BigDecimal periodInterestRateForRepaymentPeriod,
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
final List<LocalDate> scheduledDates,
final Set<LoanCharge> loanCharges) {
private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
// 1. we know 'principal' must be equal over repayments so use this to calculate interest and thus total repayment due.
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
Money totalDuePerInstallment = Money.zero(monetaryCurrency);
@Override
public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo,
final LocalDate disbursementDate, final LocalDate interestCalculatedFrom,
final BigDecimal periodInterestRateForRepaymentPeriod, final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
final List<LocalDate> scheduledDates, final Set<LoanCharge> loanCharges) {
Money outstandingBalance = loanScheduleInfo.getPrincipal();
Money principalDisbursed = loanScheduleInfo.getPrincipal();
Money totalPrincipal = Money.zero(monetaryCurrency);
Money totalInterest = Money.zero(monetaryCurrency);
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator.calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(),
loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates, idealDisbursementDateBasedOnFirstRepaymentDate);
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueAtDisbursement()) {
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
}
}
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
// create entries of disbursement period on loan schedule
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0);
BigDecimal cumulativePrincipalDisbursed = principalDisbursed.getAmount();
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
LocalDate startDate = disbursementDate;
int periodNumber = 1;
for (LocalDate scheduledDueDate : scheduledDates) {
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
// number of days from startDate to this scheduledDate
int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime()).getDays();
Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn(outstandingBalance, periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo);
Money principalForInstallment = this.periodicInterestRateCalculator.calculatePrincipalOn(totalDuePerInstallment, interestForInstallment, loanScheduleInfo);
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestForInstallment;
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction - Integer.valueOf(1).doubleValue();
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25") && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestForInstallment.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0");
}
totalPrincipal = totalPrincipal.plus(principalForInstallment);
totalInterest = totalInterest.plus(interestForInstallment);
if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
if (principalDifference.isLessThanZero()) {
principalForInstallment = principalForInstallment.plus(principalDifference.abs());
} else if (principalDifference.isGreaterThanZero()) {
principalForInstallment = principalForInstallment.minus(principalDifference.abs());
}
}
// 1. we know 'principal' must be equal over repayments so use this to
// calculate interest and thus total repayment due.
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
Money totalDuePerInstallment = Money.zero(monetaryCurrency);
outstandingBalance = outstandingBalance.minus(principalForInstallment);
Money feeChargesForInstallment = Money.zero(monetaryCurrency);
Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate,
principalForInstallment.getAmount(),
outstandingBalance.getAmount(),
interestForInstallment.getAmount(),
feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount());
Money outstandingBalance = loanScheduleInfo.getPrincipal();
Money principalDisbursed = loanScheduleInfo.getPrincipal();
Money totalPrincipal = Money.zero(monetaryCurrency);
Money totalInterest = Money.zero(monetaryCurrency);
periods.add(installment);
// handle cumulative fields
loanTermInDays += daysInPeriod;
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
startDate = scheduledDueDate;
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
.calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(),
loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates,
idealDisbursementDateBasedOnFirstRepaymentDate);
periodNumber++;
}
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
CurrencyData currencyData = new CurrencyData(
currency.getCode(),
currency.getName(),
monetaryCurrency.getDigitsAfterDecimal(),
currency.getDisplaySymbol(),
currency.getNameCode());
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
}
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueAtDisbursement()) {
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
}
}
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
// create entries of disbursement period on loan schedule
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate,
principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0);
BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount();
BigDecimal totalPrincipalExpected = BigDecimal.ZERO;
BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
BigDecimal totalInterestCharged = BigDecimal.ZERO;
BigDecimal totalFeeChargesCharged = BigDecimal.ZERO;
BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO;
BigDecimal totalWaived = BigDecimal.ZERO;
BigDecimal totalWrittenOff = BigDecimal.ZERO;
BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
BigDecimal totalRepayment = BigDecimal.ZERO;
BigDecimal totalOutstanding = BigDecimal.ZERO;
LocalDate startDate = disbursementDate;
int periodNumber = 1;
for (LocalDate scheduledDueDate : scheduledDates) {
// number of days from startDate to this scheduledDate
int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime())
.getDays();
Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn(outstandingBalance,
periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo);
Money principalForInstallment = this.periodicInterestRateCalculator.calculatePrincipalOn(totalDuePerInstallment,
interestForInstallment, loanScheduleInfo);
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestForInstallment;
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction
- Integer.valueOf(1).doubleValue();
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25")
&& interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
Money graceOnInterestForRepaymentPeriod = interestForInstallment
.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0");
}
totalPrincipal = totalPrincipal.plus(principalForInstallment);
totalInterest = totalInterest.plus(interestForInstallment);
if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
if (principalDifference.isLessThanZero()) {
principalForInstallment = principalForInstallment.plus(principalDifference.abs());
} else if (principalDifference.isGreaterThanZero()) {
principalForInstallment = principalForInstallment.minus(principalDifference.abs());
}
}
outstandingBalance = outstandingBalance.minus(principalForInstallment);
Money feeChargesForInstallment = Money.zero(monetaryCurrency);
Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate,
principalForInstallment.getAmount(), outstandingBalance.getAmount(), interestForInstallment.getAmount(),
feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount());
periods.add(installment);
// handle cumulative fields
loanTermInDays += daysInPeriod;
totalPrincipalExpected = totalPrincipalExpected.add(principalForInstallment.getAmount());
totalInterestCharged = totalInterestCharged.add(interestForInstallment.getAmount());
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
startDate = scheduledDueDate;
periodNumber++;
}
final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(),
monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode());
return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected,
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff,
totalRepaymentExpected, totalRepayment, totalOutstanding);
}
}
@@ -75,10 +75,17 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0);
BigDecimal cumulativePrincipalDisbursed = principalDisbursed.getAmount();
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
BigDecimal totalPrincipalDisbursed = principalDisbursed.getAmount();
BigDecimal totalPrincipalExpected = BigDecimal.ZERO;
BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
BigDecimal totalInterestCharged = BigDecimal.ZERO;
BigDecimal totalFeeChargesCharged = BigDecimal.ZERO;
BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO;
BigDecimal totalWaived = BigDecimal.ZERO;
BigDecimal totalWrittenOff = BigDecimal.ZERO;
BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
BigDecimal totalRepayment = BigDecimal.ZERO;
BigDecimal totalOutstanding = BigDecimal.ZERO;
LocalDate startDate = disbursementDate;
int periodNumber = 1;
@@ -128,21 +135,20 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
// handle cumulative fields
loanTermInDays += daysInPeriod;
cumulativePrincipalDue = cumulativePrincipalDue.add(principalPerInstallment.getAmount());
cumulativeInterestExpected = cumulativeInterestExpected.add(interestPerInstallment.getAmount());
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
totalPrincipalExpected = totalPrincipalExpected.add(principalPerInstallment.getAmount());
totalInterestCharged = totalInterestCharged.add(interestPerInstallment.getAmount());
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
startDate = scheduledDueDate;
periodNumber++;
}
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
final CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(),
monetaryCurrency.getDigitsAfterDecimal(), currency.getDisplaySymbol(), currency.getNameCode());
CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(),
currency.getDisplaySymbol(), currency.getNameCode());
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
return new LoanScheduleData(currencyData, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected,
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalWaived, totalWrittenOff,
totalRepaymentExpected, totalRepayment, totalOutstanding);
}
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
@@ -33,8 +33,10 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateral;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
@@ -76,6 +78,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final LoanCollateralAssembler loanCollateralAssembler;
private final AprCalculator aprCalculator;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@Autowired
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
@@ -85,7 +89,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LoanCollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository,
final NoteRepository noteRepository, final LoanScheduleCalculationPlatformService calculationPlatformService,
final ClientRepository clientRepository, final LoanProductRepository loanProductRepository,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory) {
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) {
this.context = context;
this.fromJsonHelper = fromJsonHelper;
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
@@ -100,6 +105,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
this.clientRepository = clientRepository;
this.loanProductRepository = loanProductRepository;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -231,7 +238,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
existingLoanApplication.updateLoanSchedule(loanSchedule);
existingLoanApplication.updateLoanScheduleDependentDerivedFields();
}
this.loanRepository.saveAndFlush(existingLoanApplication);
@@ -439,6 +445,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private Loan retrieveLoanBy(final Long loanId) {
final Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) { throw new LoanNotFoundException(loanId); }
loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
return loan;
}
}
@@ -34,7 +34,9 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCollateral;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
@@ -63,13 +65,17 @@ public class LoanAssembler {
private final LoanChargeAssembler loanChargeAssembler;
private final LoanCollateralAssembler loanCollateralAssembler;
private final FromJsonHelper fromApiJsonHelper;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@Autowired
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository,
final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler, final LoanCollateralAssembler loanCollateralAssembler) {
final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler,
final LoanCollateralAssembler loanCollateralAssembler, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
@@ -81,6 +87,8 @@ public class LoanAssembler {
this.loanScheduleAssembler = loanScheduleAssembler;
this.loanChargeAssembler = loanChargeAssembler;
this.loanCollateralAssembler = loanCollateralAssembler;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
}
public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) {
@@ -148,8 +156,11 @@ public class LoanAssembler {
final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
if (loanApplication == null) { throw new IllegalStateException("No loan application exists for either a client or group (or both)."); }
loanApplication.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
this.loanRepaymentScheduleTransactionProcessorFactory);
loanApplication.loanApplicationSubmittal(currentUser, loanSchedule, defaultLoanLifecycleStateMachine(), submittedOnDate, externalId);
loanApplication
.loanApplicationSubmittal(currentUser, loanSchedule, defaultLoanLifecycleStateMachine(), submittedOnDate, externalId);
return loanApplication;
}
@@ -158,7 +169,7 @@ public class LoanAssembler {
List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
public CodeValue findCodeValueByIdIfProvided(final Long codeValueId) {
CodeValue codeValue = null;
if (codeValueId != null) {
@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
@@ -31,6 +32,7 @@ import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData;
import org.mifosplatform.portfolio.loanaccount.data.LoanApplicationTimelineData;
import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
import org.mifosplatform.portfolio.loanaccount.data.LoanSummaryData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
@@ -47,8 +49,10 @@ import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@@ -90,11 +94,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
String sql = "select " + rm.loanSchema() + " where l.id = ?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId });
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
}
}
@Override
@@ -104,85 +106,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
try {
context.authenticatedUser();
final DisbursementData disbursement = repaymentScheduleRelatedLoanData.disbursementData();
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
repaymentScheduleRelatedLoanData);
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement);
final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment";
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(
disbursement.disbursementDate(), disbursement.amount(),
repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement(), disbursement.isDisbursed());
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 = wrapper.deriveCumulativeLoanTermInDays();
final BigDecimal cumulativePrincipalDisbursed = wrapper.deriveCumulativePrincipalDisbursed();
final BigDecimal cumulativePrincipalDue = wrapper.deriveCumulativePrincipalDue();
final BigDecimal cumulativePrincipalPaid = wrapper.deriveCumulativePrincipalPaid();
final BigDecimal cumulativePrincipalWrittenOff = wrapper.deriveCumulativePrincipalWrittenOff();
final BigDecimal cumulativePrincipalOutstanding = wrapper.deriveCumulativePrincipalOutstanding();
final BigDecimal cumulativeInterestExpected = wrapper.deriveCumulativeInterestExpected();
final BigDecimal cumulativeInterestPaid = wrapper.deriveCumulativeInterestPaid();
final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived();
final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff();
final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding();
final BigDecimal cumulativeFeeChargesExpected = wrapper.deriveCumulativeFeeChargesToDate();
final BigDecimal cumulativeFeeChargesPaid = wrapper.deriveCumulativeFeeChargesPaid();
final BigDecimal cumulativeFeeChargesWaived = wrapper.deriveCumulativeFeeChargesWaived();
final BigDecimal cumulativeFeeChargesWrittenOff = wrapper.deriveCumulativeFeeChargesWrittenOff();
final BigDecimal cumulativeFeeChargesOutstanding = wrapper.deriveCumulativeFeeChargesOutstanding();
final BigDecimal cumulativePenaltyChargesExpected = wrapper.deriveCumulativePenaltyChargesToDate();
final BigDecimal cumulativePenaltyChargesPaid = wrapper.deriveCumulativePenaltyChargesPaid();
final BigDecimal cumulativePenaltyChargesWaived = wrapper.deriveCumulativePenaltyChargesWaived();
final BigDecimal cumulativePenaltyChargesWrittenOff = wrapper.deriveCumulativePenaltyChargesWrittenOff();
final BigDecimal cumulativePenaltyChargesOutstanding = wrapper.deriveCumulativePenaltyChargesOutstanding();
final BigDecimal totalExpectedCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesExpected).add(
cumulativePenaltyChargesExpected);
final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalExpectedCostOfLoan);
final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeFeeChargesPaid)
.add(cumulativePenaltyChargesPaid);
final BigDecimal totalWaivedToDate = cumulativeInterestWaived.add(cumulativeFeeChargesWaived).add(
cumulativePenaltyChargesWaived);
final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff)
.add(cumulativeFeeChargesWrittenOff).add(cumulativePenaltyChargesWrittenOff);
final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding)
.add(cumulativeFeeChargesOutstanding).add(cumulativePenaltyChargesOutstanding);
final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue();
final CurrencyData currency = repaymentScheduleRelatedLoanData.getCurrency();
final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(currency.code(), currency.decimalPlaces());
final Money tolerance = Money.of(monetaryCurrency, repaymentScheduleRelatedLoanData.getInArrearsTolerance());
final Money totalOverdueMoney = Money.of(monetaryCurrency, totalOverdue);
boolean isWaiveAllowed = totalOverdueMoney.isGreaterThanZero()
&& (tolerance.isGreaterThan(totalOverdueMoney) || tolerance.isEqualTo(totalOverdueMoney));
BigDecimal totalInArrears = null;
if (!isWaiveAllowed) {
totalInArrears = totalOverdueMoney.getAmount();
}
return new LoanScheduleData(currency, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding, cumulativeInterestExpected,
cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding,
cumulativeFeeChargesExpected, cumulativeFeeChargesPaid, cumulativeFeeChargesWaived, cumulativeFeeChargesWrittenOff,
cumulativeFeeChargesOutstanding, cumulativePenaltyChargesExpected, cumulativePenaltyChargesPaid,
cumulativePenaltyChargesWaived, cumulativePenaltyChargesWrittenOff, cumulativePenaltyChargesOutstanding,
totalExpectedCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate,
totalOutstanding, totalInArrears);
return this.jdbcTemplate.query(sql, fullResultsetExtractor, new Object[] { loanId });
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
}
@@ -342,9 +270,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.approvedon_date as approvedOnDate, abu.username as approvedByUsername, abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname,"
+ " l.expected_disbursedon_date as expectedDisbursementDate, l.disbursedon_date as actualDisbursementDate, dbu.username as disbursedByUsername, dbu.firstname as disbursedByFirstname, dbu.lastname as disbursedByLastname,"
+ " l.closedon_date as closedOnDate, cbu.username as closedByUsername, cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,"
+ " l.expected_firstrepaymenton_date as expectedFirstRepaymentOnDate, l.interest_calculated_from_date as interestChargedFromDate, l.expected_maturedon_date as expectedMaturityDate, "
+ " l.total_charges_due_at_disbursement_derived as totalDisbursementCharges,"
+ " l.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery,"
+ " l.nominal_interest_rate_per_period as interestRatePerPeriod, l.annual_nominal_interest_rate as annualInterestRate, "
+ " l.repayment_period_frequency_enum as repaymentFrequencyType, l.interest_period_frequency_enum as interestRateFrequencyType, "
@@ -352,10 +278,47 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.amortization_method_enum as amortizationType, l.interest_method_enum as interestType, l.interest_calculated_in_period_enum as interestCalculationPeriodType,"
+ " l.loan_status_id as lifeCycleStatusId, l.loan_transaction_strategy_id as transactionStrategyId, "
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName" + " from m_loan l"
+ " left join m_client c on c.id = l.client_id" + " left join m_group g on g.id = l.group_id"
+ " join m_product_loan lp on lp.id = l.product_id" + " join m_currency rc on rc.`code` = l.currency_code"
+ " left join m_fund f on f.id = l.fund_id" + " left join m_staff s on s.id = l.loan_officer_id"
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, "
+ " l.principal_disbursed_derived as principalDisbursed,"
+ " l.principal_repaid_derived as principalPaid,"
+ " l.principal_writtenoff_derived as principalWrittenOff,"
+ " l.principal_outstanding_derived as principalOutstanding,"
+ " l.principal_overdue_derived as principalOverdue,"
+ " l.interest_charged_derived as interestCharged,"
+ " l.interest_repaid_derived as interestPaid,"
+ " l.interest_waived_derived as interestWaived,"
+ " l.interest_writtenoff_derived as interestWrittenOff,"
+ " l.interest_outstanding_derived as interestOutstanding,"
+ " l.interest_overdue_derived as interestOverdue,"
+ " l.fee_charges_charged_derived as feeChargesCharged,"
+ " l.total_charges_due_at_disbursement_derived as feeChargesDueAtDisbursementCharged,"
+ " l.fee_charges_repaid_derived as feeChargesPaid,"
+ " l.fee_charges_waived_derived as feeChargesWaived,"
+ " l.fee_charges_writtenoff_derived as feeChargesWrittenOff,"
+ " l.fee_charges_outstanding_derived as feeChargesOutstanding,"
+ " l.fee_charges_overdue_derived as feeChargesOverdue,"
+ " l.penalty_charges_charged_derived as penaltyChargesCharged,"
+ " l.penalty_charges_repaid_derived as penaltyChargesPaid,"
+ " l.penalty_charges_waived_derived as penaltyChargesWaived,"
+ " l.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff,"
+ " l.penalty_charges_outstanding_derived as penaltyChargesOutstanding,"
+ " l.penalty_charges_overdue_derived as penaltyChargesOverdue,"
+ " l.total_expected_repayment_derived as totalExpectedRepayment,"
+ " l.total_repayment_derived as totalRepayment,"
+ " l.total_expected_costofloan_derived as totalExpectedCostOfLoan,"
+ " l.total_costofloan_derived as totalCostOfLoan,"
+ " l.total_waived_derived as totalWaived,"
+ " l.total_writtenoff_derived as totalWrittenOff,"
+ " l.total_outstanding_derived as totalOutstanding,"
+ " l.total_overdue_derived as totalOverdue,"
+ " l.overdue_since_date_derived as overdueSinceDate"
+ " from m_loan l" //
+ " left join m_client c on c.id = l.client_id" //
+ " left join m_group g on g.id = l.group_id" //
+ " join m_product_loan lp on lp.id = l.product_id" //
+ " join m_currency rc on rc.`code` = l.currency_code" //
+ " left join m_fund f on f.id = l.fund_id" //
+ " left join m_staff s on s.id = l.loan_officer_id" //
+ " left join m_appuser sbu on sbu.id = l.submittedon_userid"
+ " left join m_appuser rbu on rbu.id = l.rejectedon_userid"
+ " left join m_appuser wbu on wbu.id = l.withdrawnon_userid"
@@ -443,7 +406,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal principal = rs.getBigDecimal("principal");
final BigDecimal inArrearsTolerance = rs.getBigDecimal("inArrearsTolerance");
final BigDecimal totalFeeChargesAtDisbursement = rs.getBigDecimal("totalDisbursementCharges");
final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaymentEvery");
@@ -478,106 +440,212 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LocalDate expectedFirstRepaymentOnDate = JdbcSupport.getLocalDate(rs, "expectedFirstRepaymentOnDate");
final LocalDate interestChargedFromDate = JdbcSupport.getLocalDate(rs, "interestChargedFromDate");
final BigDecimal feeChargesDueAtDisbursementCharged = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs,
"feeChargesDueAtDisbursementCharged");
LoanSummaryData loanSummary = null;
if (status.id().intValue() >= 300) {
// loan summary
final BigDecimal principalDisbursed = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDisbursed");
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
final BigDecimal principalOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalOutstanding");
final BigDecimal principalOverdue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalOverdue");
final BigDecimal interestCharged = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestCharged");
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
final BigDecimal interestOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestOutstanding");
final BigDecimal interestOverdue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestOverdue");
final BigDecimal feeChargesCharged = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesCharged");
final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid");
final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff");
final BigDecimal feeChargesOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesOutstanding");
final BigDecimal feeChargesOverdue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesOverdue");
final BigDecimal penaltyChargesCharged = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesCharged");
final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid");
final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived");
final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff");
final BigDecimal penaltyChargesOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesOutstanding");
final BigDecimal penaltyChargesOverdue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesOverdue");
final BigDecimal totalExpectedRepayment = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalExpectedRepayment");
final BigDecimal totalRepayment = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalRepayment");
final BigDecimal totalExpectedCostOfLoan = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalExpectedCostOfLoan");
final BigDecimal totalCostOfLoan = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalCostOfLoan");
final BigDecimal totalWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalWaived");
final BigDecimal totalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalWrittenOff");
final BigDecimal totalOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalOutstanding");
final BigDecimal totalOverdue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalOverdue");
final LocalDate overdueSinceDate = JdbcSupport.getLocalDate(rs, "overdueSinceDate");
loanSummary = new LoanSummaryData(currencyData, principalDisbursed, principalPaid, principalWrittenOff,
principalOutstanding, principalOverdue, interestCharged, interestPaid, interestWaived, interestWrittenOff,
interestOutstanding, interestOverdue, feeChargesCharged, feeChargesDueAtDisbursementCharged, feeChargesPaid,
feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, feeChargesOverdue, penaltyChargesCharged,
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding,
penaltyChargesOverdue, totalExpectedRepayment, totalRepayment, totalExpectedCostOfLoan, totalCostOfLoan,
totalWaived, totalWrittenOff, totalOutstanding, totalOverdue, overdueSinceDate);
}
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupId,
groupName, groupOfficeId, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId,
loanPurposeName, loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency,
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId,
amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
interestCalculationPeriodType, expectedFirstRepaymentOnDate, interestChargedFromDate, timeline,
totalFeeChargesAtDisbursement);
interestCalculationPeriodType, expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, loanSummary,
feeChargesDueAtDisbursementCharged);
}
}
private static final class LoanScheduleMapper implements RowMapper<LoanSchedulePeriodData> {
private static final class LoanScheduleResultSetExtractor implements ResultSetExtractor<LoanScheduleData> {
private final CurrencyData currency;
private final DisbursementData disbursement;
private final BigDecimal totalFeeChargesDueAtDisbursement;
private LocalDate lastDueDate;
private BigDecimal outstandingLoanPrincipalBalance;
public LoanScheduleMapper(final DisbursementData disbursement) {
public LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData) {
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
this.lastDueDate = disbursement.disbursementDate();
this.outstandingLoanPrincipalBalance = disbursement.amount();
}
public String loanScheduleSchema() {
public String schema() {
return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, "
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, "
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, "
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
+ " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff "
+ " from m_loan l " + " join m_loan_repayment_schedule ls on ls.loan_id = l.id ";
+ " from m_loan_repayment_schedule ls ";
}
@Override
public LoanSchedulePeriodData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public LoanScheduleData extractData(final ResultSet rs) throws SQLException, DataAccessException {
final Long loanId = rs.getLong("loanId");
final Integer period = JdbcSupport.getInteger(rs, "period");
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(
this.disbursement.disbursementDate(), this.disbursement.amount(), this.totalFeeChargesDueAtDisbursement,
this.disbursement.isDisbursed());
// TODO - KW - rather than calculate this here should we put
// calculation on derived column on table like other columns?
final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
periods.add(disbursementPeriod);
final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.code(), this.currency.decimalPlaces());
final BigDecimal totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount();
Money totalPrincipalExpected = Money.zero(monCurrency);
Money totalPrincipalPaid = Money.zero(monCurrency);
Money totalInterestCharged = Money.zero(monCurrency);
Money totalFeeChargesCharged = Money.zero(monCurrency);
Money totalPenaltyChargesCharged = Money.zero(monCurrency);
Money totalWaived = Money.zero(monCurrency);
Money totalWrittenOff = Money.zero(monCurrency);
Money totalRepaymentExpected = Money.zero(monCurrency);
Money totalRepayment = Money.zero(monCurrency);
Money totalOutstanding = Money.zero(monCurrency);
// TODO - KW - rather than calculate this here should we put
// calculation on derived column on table like other columns?
final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid);
Integer loanTermInDays = Integer.valueOf(0);
while (rs.next()) {
final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue");
final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid");
final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff");
final Long loanId = rs.getLong("loanId");
final Integer period = JdbcSupport.getInteger(rs, "period");
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff);
final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid);
Integer daysInPeriod = Integer.valueOf(0);
if (fromDate != null) {
daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
loanTermInDays = Integer.valueOf(loanTermInDays.intValue() + daysInPeriod.intValue());
}
final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue");
final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid");
final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived");
final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff");
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
totalPrincipalExpected = totalPrincipalExpected.plus(principalDue);
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
totalPrincipalPaid = totalPrincipalPaid.plus(principalPaid);
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract(
penaltyChargesWrittenOff);
final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid);
final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(
penaltyChargesExpectedDue);
final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
totalInterestCharged = totalInterestCharged.plus(interestExpectedDue);
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod);
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid).add(penaltyChargesPaid);
final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived).add(penaltyChargesWaived);
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff)
.add(penaltyChargesWrittenOff);
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding)
.add(penaltyChargesOutstanding);
final BigDecimal interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid);
final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue);
final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue");
totalFeeChargesCharged = totalFeeChargesCharged.plus(feeChargesExpectedDue);
final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid");
final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff");
if (fromDate == null) {
fromDate = this.lastDueDate;
final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff);
final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid);
final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue");
totalPenaltyChargesCharged = totalPenaltyChargesCharged.plus(penaltyChargesExpectedDue);
final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid");
final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived");
final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff");
final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract(
penaltyChargesWrittenOff);
final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid);
final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(
penaltyChargesExpectedDue);
final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod);
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid).add(penaltyChargesPaid);
final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived).add(penaltyChargesWaived);
totalWaived = totalWaived.plus(totalWaivedForPeriod);
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff)
.add(penaltyChargesWrittenOff);
totalWrittenOff = totalWrittenOff.plus(totalWrittenOffForPeriod);
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding)
.add(penaltyChargesOutstanding);
final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue);
totalRepaymentExpected = totalRepaymentExpected.plus(totalDueForPeriod);
totalRepayment = totalRepayment.plus(totalPaidForPeriod);
totalOutstanding = totalOutstanding.plus(totalOutstandingForPeriod);
if (fromDate == null) {
fromDate = this.lastDueDate;
}
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
// update based on current period values
this.lastDueDate = dueDate;
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate,
dueDate, principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan,
interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue,
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue,
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod,
totalActualCostOfLoanForPeriod);
periods.add(periodData);
}
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
// update based on current period values
this.lastDueDate = dueDate;
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, principalDue, principalPaid,
principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestExpectedDue, interestPaid,
interestWaived, interestWrittenOff, interestOutstanding, feeChargesExpectedDue, feeChargesPaid, feeChargesWaived,
feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived,
penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod,
totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod);
return new LoanScheduleData(this.currency, periods, loanTermInDays, totalPrincipalDisbursed,
totalPrincipalExpected.getAmount(), totalPrincipalPaid.getAmount(), totalInterestCharged.getAmount(),
totalFeeChargesCharged.getAmount(), totalPenaltyChargesCharged.getAmount(), totalWaived.getAmount(),
totalWrittenOff.getAmount(), totalRepaymentExpected.getAmount(), totalRepayment.getAmount(),
totalOutstanding.getAmount());
}
}
@@ -1,198 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.util.Collection;
import org.mifosplatform.portfolio.loanaccount.loanschedule.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 deriveCumulativePrincipalWrittenOff() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.principalWrittenOff());
}
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 deriveCumulativeInterestWrittenOff() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestWrittenOff());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeInterestOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.interestOutstanding());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeFeeChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.feeChargesDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeFeeChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.feeChargesPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeFeeChargesWaived() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.feeChargesWaived());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeFeeChargesWrittenOff() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.feeChargesWrittenOff());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeFeeChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.feeChargesOutstanding());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesWaived() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesWaived());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesWrittenOff() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesWrittenOff());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesOutstanding());
}
return cumulativeValue;
}
public Integer deriveCumulativeLoanTermInDays() {
Integer cumulativeValue = Integer.valueOf(0);
for (LoanSchedulePeriodData period : this.periods) {
if (period.daysInPeriod() != null) {
cumulativeValue += period.daysInPeriod();
}
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeTotalOverdue() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.totalOverdue());
}
return cumulativeValue;
}
}
@@ -33,8 +33,10 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
@@ -63,6 +65,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanChargeRepository loanChargeRepository;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -70,9 +74,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final NoteRepository noteRepository, final ChargeRepositoryWrapper chargeRepository,
final LoanChargeRepository loanChargeRepository,
final ApplicationCurrencyRepository applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepository applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler;
@@ -84,6 +88,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -105,8 +111,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.disburse(currentUser, command, defaultLoanLifecycleStateMachine(), currency,
existingTransactionIds, existingReversedTransactionIds);
final Map<String, Object> changes = loan.disburse(currentUser, command, currency, existingTransactionIds,
existingReversedTransactionIds);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
@@ -142,8 +148,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = loan.undoDisbursal(defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds);
final Map<String, Object> changes = loan.undoDisbursal(existingTransactionIds, existingReversedTransactionIds);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
@@ -576,6 +581,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private Loan retrieveLoanBy(final Long loanId) {
final Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) { throw new LoanNotFoundException(loanId); }
loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
return loan;
}