diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index a6608b6cc..53bee542d 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -11,7 +11,7 @@ var result = {}; if (window.location.search) { // split up the query string and store in an associative array - var params = window.location.search.slice(1).split("&"); x + var params = window.location.search.slice(1).split("&"); for ( var i = 0; i < params.length; i++) { var tmp = params[i].split("="); result[tmp[0]] = unescape(tmp[1]); @@ -3821,6 +3821,34 @@ GET https://DomainName/mifosng-provider/api/v1/groups Example Values: 2=Per month, 3=Per year +
Optional Fields |
+
| graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged | +
@@ -6967,6 +7004,28 @@ Request Body:
+
+ graceOnPrincipalPayment
+
+
+ Optional: Integer - represents the number of repayment periods that grace should apply to the principal component of a repayment period.
+
+
+
+ graceOnInterestPayment
+
+
+ Optional: Integer - represents the number of repayment periods that grace should apply to the interest component of a repayment period. Interest is still calculated but offset to later repayment periods.
+
+
+
+ graceOnInterestCharged
+
+
+ Optional: Integer - represents the number of repayment periods that should be interest-free.
+
+
+
accountingRule
@@ -7524,13 +7583,14 @@ GET https://DomainName/api/v1/loanproducts/{productId}
name, currencyCode, digitsAfterDecimal, principal,
numberOfRepayments, repaymentEvery, repaymentFrequencyType,
interestRatePerPeriod, interestRateFrequencyType, amortizationType, interestType,
- interestCalculationPeriodType, transactionProcessingStrategyId, accountingRule
+ interestCalculationPeriodType, transactionProcessingStrategyId,
+ accountingRule
Optional Fields
- inArrearsTolerance, charges,paymentChannelToFundSourceMappings
+ inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings
diff --git a/mifosng-db/migrations/core_db/V50__add-grace-settings-to-loan-product.sql b/mifosng-db/migrations/core_db/V50__add-grace-settings-to-loan-product.sql
new file mode 100644
index 000000000..bef3665d4
--- /dev/null
+++ b/mifosng-db/migrations/core_db/V50__add-grace-settings-to-loan-product.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `m_product_loan`
+ADD COLUMN `grace_on_principal_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `max_number_of_repayments`,
+ADD COLUMN `grace_on_interest_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_principal_periods`,
+ADD COLUMN `grace_interest_free_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_interest_periods`;
+
+ALTER TABLE `m_loan`
+ADD COLUMN `grace_on_principal_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `number_of_repayments`,
+ADD COLUMN `grace_on_interest_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_principal_periods`,
+ADD COLUMN `grace_interest_free_periods` SMALLINT(5) NULL DEFAULT NULL AFTER `grace_on_interest_periods`;
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java
index 751a803f0..7416dee50 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java
@@ -1,3 +1,8 @@
+/**
+ * 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.accounting.glaccount.data;
public class GLAccountDataForLookup {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java
index ca8e203a2..45d59db3e 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/JdbcSupport.java
@@ -47,6 +47,19 @@ public class JdbcSupport {
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
}
+ public static Integer getIntegerDefaultToNullIfZero(final ResultSet rs, final String columnName) throws SQLException {
+ Integer value = (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
+ return defaultToNullIfZero(value);
+ }
+
+ private static Integer defaultToNullIfZero(final Integer value) {
+ Integer result = value;
+ if (result != null && Integer.valueOf(0).equals(value)) {
+ result = null;
+ }
+ return result;
+ }
+
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
final BigDecimal value = rs.getBigDecimal(columnName);
return defaultToZeroIfNull(value);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/LocalDateInterval.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/LocalDateInterval.java
similarity index 96%
rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/LocalDateInterval.java
rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/LocalDateInterval.java
index 84fef927b..80dd741ff 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/LocalDateInterval.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/LocalDateInterval.java
@@ -3,7 +3,7 @@
* 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.savings.domain;
+package org.mifosplatform.infrastructure.core.domain;
import org.joda.time.Days;
import org.joda.time.LocalDate;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java
index e7d5e189d..bdf28007d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/ApplicationCurrency.java
@@ -77,6 +77,10 @@ public class ApplicationCurrency extends AbstractPersistable {
return new CurrencyData(this.code, this.name, this.decimalPlaces, this.displaySymbol, this.nameCode);
}
+ public CurrencyData toData(final int digitsAfterDecimalSupported) {
+ return new CurrencyData(this.code, this.name, digitsAfterDecimalSupported, this.displaySymbol, this.nameCode);
+ }
+
public OrganisationCurrency toOrganisationCurrency() {
return new OrganisationCurrency(this.code, this.name, this.decimalPlaces, this.nameCode, this.displaySymbol);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
index 9a66837ee..87768c984 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java
@@ -256,4 +256,8 @@ public class Money implements Comparable {
private MonetaryCurrency monetaryCurrency() {
return new MonetaryCurrency(this.currencyCode, this.currencyDigitsAfterDecimal);
}
+
+ public Money zero() {
+ return Money.zero(getCurrency());
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java
index 3059f5df6..0adbb3507 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java
@@ -64,6 +64,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.NotSupportedLoanTemplat
import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData;
import org.mifosplatform.portfolio.loanaccount.guarantor.service.GuarantorReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
@@ -88,12 +89,12 @@ public class LoansApiResource {
"loanPurposeName", "loanOfficerId", "loanOfficerName", "currency", "principal", "inArrearsTolerance", "termFrequency",
"termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "transactionProcessingStrategyId", "interestRateFrequencyType", "amortizationType", "interestType",
- "interestCalculationPeriodType", "expectedFirstRepaymentOnDate", "interestChargedFromDate", "timeline",
- "totalFeeChargesAtDisbursement", "summary", "repaymentSchedule", "transactions", "charges", "collateral", "guarantors","meeting",
- "productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
- "repaymentFrequencyTypeOptions", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions",
- "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions",
- "chargeTemplate","calendarOptions"));
+ "interestCalculationPeriodType", "expectedFirstRepaymentOnDate", "graceOnPrincipalPayment", "graceOnInterestPayment",
+ "graceOnInterestCharged", "interestChargedFromDate", "timeline", "totalFeeChargesAtDisbursement", "summary",
+ "repaymentSchedule", "transactions", "charges", "collateral", "guarantors", "meeting", "productOptions",
+ "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions",
+ "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
+ "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions"));
private final String resourceNameForPermissions = "LOAN";
@@ -126,8 +127,7 @@ public class LoansApiResource {
final CollateralReadPlatformService loanCollateralReadPlatformService,
final LoanScheduleCalculationPlatformService calculationPlatformService,
final StaffReadPlatformService staffReadPlatformService, final GuarantorReadPlatformService guarantorReadPlatformService,
- final CodeValueReadPlatformService codeValueReadPlatformService,
- final GroupReadPlatformService groupReadPlatformService,
+ final CodeValueReadPlatformService codeValueReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
final DefaultToApiJsonSerializer toApiJsonSerializer,
final DefaultToApiJsonSerializer loanScheduleToApiJsonSerializer,
final DefaultToApiJsonSerializer loanOfficeTransferToApiJsonSerializer,
@@ -161,7 +161,8 @@ public class LoansApiResource {
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
- @QueryParam("productId") final Long productId, @QueryParam("templateType") final String templateType, @Context final UriInfo uriInfo) {
+ @QueryParam("productId") final Long productId, @QueryParam("templateType") final String templateType,
+ @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -174,24 +175,24 @@ public class LoansApiResource {
Collection calendarOptions = null;
LoanAccountData newLoanAccount = null;
Long officeId = null;
-
+
if (productId != null) {
newLoanAccount = this.loanReadPlatformService.retrieveLoanProductDetailsTemplate(productId);
}
-
- if(templateType == null){
+
+ if (templateType == null) {
final String errorMsg = "Loan template type must be provided";
throw new LoanTemplateTypeRequiredException(errorMsg);
- }else if (templateType.equals("collateral")) {
+ } else if (templateType.equals("collateral")) {
newLoanAccount = LoanAccountData.collateralTemplate(loanCollateralOptions);
} else {
- //for JLG loan both client and group details are required
+ // for JLG loan both client and group details are required
if (templateType.equals("individual") || templateType.equals("jlg")) {
LoanAccountData loanAccountClientDetails = this.loanReadPlatformService.retrieveClientDetailsTemplate(clientId);
officeId = loanAccountClientDetails.officeId();
-
+
newLoanAccount = (newLoanAccount == null) ? loanAccountClientDetails : LoanAccountData.populateClientDefaults(
newLoanAccount, loanAccountClientDetails);
@@ -211,7 +212,7 @@ public class LoansApiResource {
loanAccountGroupData);
} else if (templateType.equals("jlgbulk")) {
- //get group details along with members in that group
+ // get group details along with members in that group
final LoanAccountData loanAccountGroupData = this.loanReadPlatformService.retrieveGroupAndMembersDetailsTemplate(groupId);
officeId = loanAccountGroupData.groupOfficeId();
calendarOptions = this.loanReadPlatformService.retrieveCalendars(groupId);
@@ -223,7 +224,8 @@ public class LoansApiResource {
}
if (officeId != null) allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId);
- // add product options, allowed loan officers and calendar options (calendar options will be null in individual loan)
+ // add product options, allowed loan officers and calendar options
+ // (calendar options will be null in individual loan)
newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions);
}
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -246,7 +248,7 @@ public class LoansApiResource {
Collection guarantors = null;
Collection collateral = null;
CalendarData meeting = null;
-
+
final Set mandatoryResponseParameters = new HashSet();
final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
@@ -293,7 +295,7 @@ public class LoansApiResource {
collateral = null;
}
}
-
+
if (associationParameters.contains("meeting")) {
mandatoryResponseParameters.add("meeting");
meeting = this.calendarReadPlatformService.retrieveLoanCalendar(loanId);
@@ -332,20 +334,20 @@ public class LoansApiResource {
final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
-
- if(loanBasicDetails.officeId() != null){
+
+ if (loanBasicDetails.officeId() != null) {
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.officeId());
- }else if(loanBasicDetails.groupOfficeId() != null){
+ } else if (loanBasicDetails.groupOfficeId() != null) {
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.groupOfficeId());
}
-
+
loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose");
loanCollateralOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanCollateral");
-
- if(loanBasicDetails.groupId() != null){
+
+ if (loanBasicDetails.groupId() != null) {
calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId());
}
-
+
}
final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
@@ -376,7 +378,7 @@ public class LoansApiResource {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanBasicDetails, LOAN_DATA_PARAMETERS);
}
-
+
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@@ -388,10 +390,10 @@ public class LoansApiResource {
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
- final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
+ final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
- return this.loanScheduleToApiJsonSerializer.serialize(settings, loanSchedule, new HashSet());
+ return this.loanScheduleToApiJsonSerializer.serialize(settings, loanSchedule.toData(), new HashSet());
}
final CommandWrapper commandRequest = new CommandWrapperBuilder().createLoanApplication().withJson(apiRequestBodyAsJson).build();
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
index d0a97a4c9..2add7b5da 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java
@@ -82,8 +82,11 @@ public class LoanAccountData {
private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId;
- private final LocalDate expectedFirstRepaymentOnDate;
+ private final Integer graceOnPrincipalPayment;
+ private final Integer graceOnInterestPayment;
+ private final Integer graceOnInterestCharged;
private final LocalDate interestChargedFromDate;
+ private final LocalDate expectedFirstRepaymentOnDate;
// timeline
private final LoanApplicationTimelineData timeline;
@@ -157,6 +160,9 @@ public class LoanAccountData {
final EnumOptionData interestType = null;
final EnumOptionData interestCalculationPeriodType = null;
final LocalDate expectedFirstRepaymentOnDate = null;
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = null;
final LoanSummaryData summary = null;
@@ -188,11 +194,11 @@ public class LoanAccountData {
currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery,
repaymentFrequencyType, transactionProcessingStrategyId, amortizationType, interestRatePerPeriod,
interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
- interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
- collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
- transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
- interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
- loanCollateralOptions, calendarOptions);
+ graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary,
+ feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
+ productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions,
+ interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
+ fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
/**
@@ -233,6 +239,9 @@ public class LoanAccountData {
final EnumOptionData interestType = null;
final EnumOptionData interestCalculationPeriodType = null;
final LocalDate expectedFirstRepaymentOnDate = null;
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = LoanApplicationTimelineData.templateDefault(expectedDisbursementDate);
final LoanSummaryData summary = null;
@@ -265,8 +274,9 @@ public class LoanAccountData {
currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery,
repaymentFrequencyType, transactionProcessingStrategyId, amortizationType, interestRatePerPeriod,
interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
- interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
- collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
+ graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary,
+ feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
+ productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
@@ -274,19 +284,20 @@ public class LoanAccountData {
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, clientAcc.clientId, clientAcc.clientName,
- clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId,
- acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
- acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
- acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod,
- acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType,
- acc.expectedFirstRepaymentOnDate, acc.interestChargedFromDate, clientAcc.timeline, acc.summary,
- acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting,
- acc.productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
+ clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
+ acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency,
+ acc.principal, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments,
+ acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType,
+ acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
+ acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment,
+ acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate, clientAcc.timeline, acc.summary,
+ acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors,
+ acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions);
}
-
+
/**
* Used to produce a {@link LoanAccountData} with only group information
* defaulted.
@@ -326,6 +337,9 @@ public class LoanAccountData {
final EnumOptionData interestType = null;
final EnumOptionData interestCalculationPeriodType = null;
final LocalDate expectedFirstRepaymentOnDate = null;
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
final LocalDate interestChargedFromDate = null;
final LoanApplicationTimelineData timeline = LoanApplicationTimelineData.templateDefault(expectedDisbursementDate);
final LoanSummaryData summary = null;
@@ -358,28 +372,30 @@ public class LoanAccountData {
currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery,
repaymentFrequencyType, transactionProcessingStrategyId, amortizationType, interestRatePerPeriod,
interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
- interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
- collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
+ graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, summary,
+ feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
+ productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
- return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName,
- acc.clientOfficeId, groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId,
- acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
+ return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
+ groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
+ acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod,
acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType,
- acc.expectedFirstRepaymentOnDate, acc.interestChargedFromDate, groupAcc.timeline, acc.summary,
- acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting,
- acc.productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
- acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
- acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
- acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions);
+ acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
+ acc.interestChargedFromDate, groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
+ acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
+ acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions,
+ acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
+ acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
+ acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions);
}
-
+
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
final Collection termFrequencyTypeOptions, final Collection repaymentFrequencyTypeOptions,
final Collection repaymentStrategyOptions,
@@ -387,7 +403,7 @@ public class LoanAccountData {
final Collection interestTypeOptions, final Collection interestCalculationPeriodTypeOptions,
final Collection fundOptions, final Collection chargeOptions,
final Collection loanPurposeOptions, final Collection loanCollateralOptions) {
-
+
final Long id = null;
final String accountNo = null;
final LoanStatusEnumData status = null;
@@ -423,7 +439,7 @@ public class LoanAccountData {
final LoanSummaryData summary = null;
final BigDecimal feeChargesDueAtDisbursementCharged = null;
final ChargeData chargeTemplate = null;
-
+
final LoanScheduleData repaymentSchedule = null;
final Collection transactions = null;
final Collection collateral = null;
@@ -432,7 +448,7 @@ public class LoanAccountData {
final Collection productOptions = null;
final Collection calendarOptions = null;
final Collection loanOfficerOptions = null;
-
+
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
@@ -441,20 +457,21 @@ public class LoanAccountData {
charges.add(charge.toLoanChargeData());
}
- return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId,
- group, loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(),
- loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(),
- product.getPrincipal(), product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType,
- product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(),
- product.getTransactionProcessingStrategyId(), product.getAmortizationType(), product.getInterestRatePerPeriod(),
- product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
- product.getInterestCalculationPeriodType(), expectedFirstRepaymentOnDate, interestChargedFromDate, timeline,
- summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
- productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
- interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
- fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
+ return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
+ product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
+ loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), product.getPrincipal(),
+ product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(),
+ product.getRepaymentEvery(), product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(),
+ product.getAmortizationType(), product.getInterestRatePerPeriod(), product.getInterestRateFrequencyType(),
+ product.getAnnualInterestRate(), product.getInterestType(), product.getInterestCalculationPeriodType(),
+ expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(), product.getGraceOnInterestPayment(),
+ product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged,
+ repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
+ repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
+ interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
+ loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
-
+
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
final LoanScheduleData repaymentSchedule = null;
@@ -477,7 +494,7 @@ public class LoanAccountData {
final Collection loanPurposeOptions = null;
final Collection loanCollateralOptions = null;
final Collection calendarOptions = null;
-
+
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
@@ -487,15 +504,16 @@ public class LoanAccountData {
}
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
- acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(),
- acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, product.getCurrency(),
- product.getPrincipal(), product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType,
+ acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(),
+ product.getFundName(), acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName,
+ product.getCurrency(), product.getPrincipal(), product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType,
product.getNumberOfRepayments(), product.getRepaymentEvery(), product.getRepaymentFrequencyType(),
product.getTransactionProcessingStrategyId(), product.getAmortizationType(), product.getInterestRatePerPeriod(),
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
- product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, acc.interestChargedFromDate, acc.timeline,
- acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
- productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
+ product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
+ product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), acc.interestChargedFromDate, acc.timeline,
+ acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors,
+ calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
@@ -506,16 +524,16 @@ public class LoanAccountData {
*/
public static LoanAccountData basicLoanDetails(final Long id, final String accountNo, final LoanStatusEnumData status,
final String externalId, final Long clientId, final String clientName, final Long clientOfficeId, final GroupGeneralData group,
- final EnumOptionData loanType, final Long loanProductId, final String loanProductName, final String loanProductDescription, final Long fundId,
- final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId,
+ final EnumOptionData loanType, final Long loanProductId, final String loanProductName, final String loanProductDescription,
+ final Long fundId, final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId,
final String loanOfficerName, final CurrencyData currencyData, final BigDecimal principal, final BigDecimal inArrearsTolerance,
final Integer termFrequency, final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments,
final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, final Long transactionStrategyId,
final EnumOptionData amortizationType, 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 LoanSummaryData loanSummary,
- final BigDecimal feeChargesDueAtDisbursementCharged) {
+ final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment,
+ final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
+ final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged) {
final LoanScheduleData repaymentSchedule = null;
final Collection transactions = null;
@@ -538,14 +556,15 @@ public class LoanAccountData {
final Collection loanPurposeOptions = null;
final Collection loanCollateralOptions = null;
final Collection calendarOptions = null;
-
+
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, 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, loanSummary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral,
- guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
+ annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment,
+ graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary,
+ feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
+ productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
@@ -566,41 +585,44 @@ public class LoanAccountData {
final Collection loanOfficerOptions, final Collection loanPurposeOptions,
final Collection loanCollateralOptions, final Collection calendarOptions) {
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
- acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, acc.loanPurposeId,
- acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, acc.inArrearsTolerance,
- acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
- acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
- acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
+ acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
+ acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
+ acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
+ acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod,
+ acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType,
+ acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule,
- transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
- transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
- interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions,
- loanCollateralOptions, calendarOptions);
+ transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
+ repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions,
+ amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions,
+ chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions);
}
- public static LoanAccountData associationsAndTemplate(LoanAccountData acc, Collection productOptions,
- Collection allowedLoanOfficers, final Collection calendarOptions) {
- return associationsAndTemplate(acc, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting,
- productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions,
- acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
- acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, allowedLoanOfficers,
- acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions);
+ public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions,
+ final Collection allowedLoanOfficers, final Collection calendarOptions) {
+ return associationsAndTemplate(acc, acc.repaymentSchedule, acc.transactions, acc.charges, acc.collateral, acc.guarantors,
+ acc.meeting, productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
+ acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
+ acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
+ allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions);
}
-
+
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
- group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName, acc.loanPurposeId,
- acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal, acc.inArrearsTolerance,
- acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
- acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
- acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
+ group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
+ acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
+ acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery,
+ acc.repaymentFrequencyType, acc.transactionProcessingStrategyId, acc.amortizationType, acc.interestRatePerPeriod,
+ acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType,
+ acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged,
acc.interestChargedFromDate, acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule,
- acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions,
- acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
- acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions,
- acc.loanCollateralOptions, acc.calendarOptions);
+ acc.transactions, acc.charges, acc.collateral, acc.guarantors, acc.meeting, acc.productOptions,
+ acc.termFrequencyTypeOptions, acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions,
+ acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
+ acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
+ acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions);
}
-
+
private LoanAccountData(
final Long id, //
final String accountNo, //
@@ -631,6 +653,7 @@ public class LoanAccountData {
final Long transactionProcessingStrategyId, final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod,
final EnumOptionData interestRateFrequencyType, final BigDecimal annualInterestRate, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final LocalDate expectedFirstRepaymentOnDate,
+ final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData summary,
final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule,
final Collection transactions, final Collection charges,
@@ -677,6 +700,9 @@ public class LoanAccountData {
this.interestType = interestType;
this.interestCalculationPeriodType = interestCalculationPeriodType;
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
+ this.graceOnPrincipalPayment = graceOnPrincipalPayment;
+ this.graceOnInterestPayment = graceOnInterestPayment;
+ this.graceOnInterestCharged = graceOnInterestCharged;
this.interestChargedFromDate = interestChargedFromDate;
this.timeline = timeline;
this.feeChargesAtDisbursementCharged = feeChargesDueAtDisbursementCharged;
@@ -736,7 +762,7 @@ public class LoanAccountData {
} else {
this.loanCollateralOptions = loanCollateralOptions;
}
-
+
if (CollectionUtils.isEmpty(calendarOptions)) {
this.calendarOptions = null;
} else {
@@ -760,12 +786,12 @@ public class LoanAccountData {
public Collection charges() {
return this.charges;
}
-
- public Long groupOfficeId(){
- return group == null? null : group.officeId();
+
+ public Long groupOfficeId() {
+ return group == null ? null : group.officeId();
}
-
- public Long groupId(){
- return group == null? null : group.getId();
+
+ public Long groupId() {
+ return group == null ? null : group.getId();
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
index b6df12500..1a59b08fa 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
@@ -6,6 +6,8 @@
package org.mifosplatform.portfolio.loanaccount.domain;
import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -61,14 +63,12 @@ import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanTransactionT
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentDateException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultLoanScheduleGeneratorFactory;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
-import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
@@ -266,21 +266,20 @@ public class Loan extends AbstractPersistable {
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
- final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
- final Set loanCharges, final Set collateral) {
+ final LoanTransactionProcessingStrategy transactionProcessingStrategy,
+ final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges,
+ final Set collateral) {
final LoanStatus status = null;
final Group group = null;
- LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
loanRepaymentScheduleDetail, status, loanCharges, collateral);
}
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer,
- final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
- final Set loanCharges) {
+ final LoanTransactionProcessingStrategy transactionProcessingStrategy,
+ final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges) {
final LoanStatus status = null;
- final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
final CodeValue loanPurpose = null;
final Set collateral = null;
final Client client = null;
@@ -290,10 +289,9 @@ public class Loan extends AbstractPersistable {
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer,
- final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanSchedule loanSchedule,
- final Set loanCharges) {
+ final LoanTransactionProcessingStrategy transactionProcessingStrategy,
+ final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges) {
final LoanStatus status = null;
- final LoanProductRelatedDetail loanRepaymentScheduleDetail = loanSchedule.loanProductRelatedDetail();
final CodeValue loanPurpose = null;
final Set collateral = null;
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
@@ -308,6 +306,10 @@ public class Loan extends AbstractPersistable {
final Staff loanOfficer, final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
final Set loanCharges, final Set collateral) {
+
+ this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
+ this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
+
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
@@ -323,7 +325,6 @@ public class Loan extends AbstractPersistable {
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.loanProduct = loanProduct;
- this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
if (loanStatus != null) {
this.loanStatus = loanStatus.getValue();
} else {
@@ -735,10 +736,10 @@ public class Loan extends AbstractPersistable {
this.collateral.addAll(associateWithThisLoan(loanCollateral));
}
- public void updateLoanSchedule(final LoanScheduleData modifiedLoanSchedule) {
+ public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
this.repaymentScheduleInstallments.clear();
- for (LoanSchedulePeriodData scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
+ for (LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
if (scheduledLoanInstallment.isRepaymentPeriod()) {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this,
@@ -750,24 +751,6 @@ public class Loan extends AbstractPersistable {
}
}
- // if the loan application/contract is modified when repayments are
- // already against it - then need to re-process it
-
- /****
- * TODO Vishwas Currently we do not allow modifying a Loan contract once
- * the loan is approved (hence there is no need to adjust any loan
- * transactions).
- *
- * Consider removing this block of code or logically completing it for
- * the future by getting the list of affected Transactions
- ***/
- final List repaymentsOrWaivers = retreiveListOfTransactionsPostDisbursement();
-
- final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
- .determineProcessor(this.transactionProcessingStrategy);
- loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), repaymentsOrWaivers, getCurrency(),
- this.repaymentScheduleInstallments, setOfLoanCharges());
-
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields();
}
@@ -1042,18 +1025,11 @@ public class Loan extends AbstractPersistable {
}
}
- public void loanApplicationSubmittal(final AppUser currentUser, final LoanSchedule loanSchedule,
- final LoanLifecycleStateMachine lifecycleStateMachine, final LocalDate submittedOn, final String externalId) {
+ public void loanApplicationSubmittal(final AppUser currentUser, final LoanScheduleModel loanSchedule,
+ final LoanApplicationTerms loanApplicationTerms, final LoanLifecycleStateMachine lifecycleStateMachine,
+ final LocalDate submittedOn, final String externalId) {
- final LoanScheduleData loanScheduleData = loanSchedule.generate();
-
- // Have to set expectedDisbursementDate to avoid nullPointer so should
- // be passed down to updateLoanSchedule method
- if (loanSchedule.getDisbursementDate() != null) {
- this.expectedDisbursementDate = loanSchedule.getDisbursementDate().toDate();
- }
-
- updateLoanSchedule(loanScheduleData);
+ updateLoanSchedule(loanSchedule);
LoanStatus from = null;
if (this.loanStatus != null) {
@@ -1064,22 +1040,16 @@ public class Loan extends AbstractPersistable {
this.loanStatus = statusEnum.getValue();
this.externalId = externalId;
-
- this.termFrequency = loanSchedule.getLoanTermFrequency();
- this.termPeriodFrequencyType = loanSchedule.getLoanTermPeriodFrequencyType().getValue();
-
+ this.termFrequency = loanApplicationTerms.getLoanTermFrequency();
+ this.termPeriodFrequencyType = loanApplicationTerms.getLoanTermPeriodFrequencyType().getValue();
this.submittedOnDate = submittedOn.toDate();
this.submittedBy = currentUser;
+ this.expectedDisbursementDate = loanApplicationTerms.getExpectedDisbursementDate().toDate();
+ this.expectedFirstRepaymentOnDate = loanApplicationTerms.getRepaymentStartFromDate();
+ this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
+
updateLoanScheduleDependentDerivedFields();
- if (loanSchedule.getRepaymentStartFromDate() != null) {
- this.expectedFirstRepaymentOnDate = loanSchedule.getRepaymentStartFromDate().toDate();
- }
-
- if (loanSchedule.getInterestChargedFromDate() != null) {
- this.interestChargedFromDate = loanSchedule.getInterestChargedFromDate().toDate();
- }
-
if (submittedOn.isAfter(DateUtils.getLocalDateOfTenant())) {
final String errorMessage = "The date on which a loan is submitted cannot be in the future.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn);
@@ -1297,9 +1267,9 @@ public class Loan extends AbstractPersistable {
return ids;
}
- public Map disburse(final AppUser currentUser, final JsonCommand command, final ApplicationCurrency currency,
- final List existingTransactionIds, final List existingReversedTransactionIds,
- final Map actualChanges, final PaymentDetail paymentDetail) {
+ public Map disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser,
+ final JsonCommand command, final ApplicationCurrency currency, final List existingTransactionIds,
+ final List existingReversedTransactionIds, final Map actualChanges, final PaymentDetail paymentDetail) {
updateLoanToPreDisbursalState();
@@ -1325,7 +1295,7 @@ public class Loan extends AbstractPersistable {
handleDisbursementTransaction(paymentDetail, actualDisbursementDate);
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) {
- regenerateRepaymentSchedule(currency);
+ regenerateRepaymentSchedule(loanScheduleFactory, currency);
} else {
updateLoanSummaryDerivedFields();
}
@@ -1349,35 +1319,25 @@ public class Loan extends AbstractPersistable {
* Ability to regenerate the repayment schedule based on the loans current
* details/state.
*/
- private void regenerateRepaymentSchedule(final ApplicationCurrency applicationCurrency) {
+ private void regenerateRepaymentSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
+ final ApplicationCurrency applicationCurrency) {
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
- final LoanScheduleGenerator loanScheduleGenerator = new DefaultLoanScheduleGeneratorFactory().create(interestMethod);
+ final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod);
- final BigDecimal principal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount();
- final BigDecimal inArrearsTolerance = this.loanRepaymentScheduleDetail.getInArrearsTolerance().getAmount();
- final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
- final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
- final BigDecimal defaultAnnualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
+ final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
+ final MathContext mc = new MathContext(8, roundingMode);
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail
- .getInterestCalculationPeriodMethod();
- final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
- final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
- final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
- final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
- 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);
+ LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
+ loanTermPeriodFrequencyType, getExpectedDisbursedOnLocalDate(), getExpectedFirstRepaymentOnDate(), getInArrearsTolerance(),
+ this.loanRepaymentScheduleDetail);
- final LoanScheduleData generatedData = loanSchedule.generate();
+ LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, this.charges);
- updateLoanSchedule(generatedData);
+ updateLoanSchedule(loanSchedule);
}
private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn) {
@@ -1433,14 +1393,6 @@ public class Loan extends AbstractPersistable {
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn);
}
- LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments.get(0).getDueDate();
- if (disbursedOn.isAfter(firstRepaymentDueDate)) {
- final String errorMessage = "The date on which a loan with identifier : " + this.accountNumber
- + " is disbursed cannot be after the first expected repayment date: " + firstRepaymentDueDate.toString();
- throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.first.repayment.due.date", errorMessage,
- this.accountNumber, disbursedOn, firstRepaymentDueDate);
- }
-
return disbursementTransaction;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
index 17bed9155..6729921d3 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
@@ -286,14 +286,6 @@ public class LoanSchedulePeriodData {
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;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
new file mode 100644
index 000000000..8a342c5cd
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -0,0 +1,188 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.joda.time.Days;
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
+
+/**
+ *
+ */
+public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGenerator {
+
+ private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
+ private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
+
+ @Override
+ public LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
+ final LoanApplicationTerms loanApplicationTerms, final Set loanCharges) {
+
+ // 1. generate list of proposed schedule due dates
+ final List scheduledDates = this.scheduledDateGenerator.generate(loanApplicationTerms);
+
+ // 2. determine the total charges due at time of disbursement
+ final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
+
+ // 3. setup variables for tracking important facts required for loan
+ // schedule generation.
+ final Money principalDisbursed = loanApplicationTerms.getPrincipal();
+ final MonetaryCurrency currency = principalDisbursed.getCurrency();
+ final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
+
+ // variables for cumulative totals
+ int loanTermInDays = Integer.valueOf(0);
+ BigDecimal totalPrincipalExpected = BigDecimal.ZERO;
+ BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
+ BigDecimal totalInterestCharged = BigDecimal.ZERO;
+ BigDecimal totalFeeChargesCharged = chargesDueAtTimeOfDisbursement;
+ BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO;
+ BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement;
+ BigDecimal totalOutstanding = BigDecimal.ZERO;
+
+ final Collection periods = createNewLoanScheduleListWithDisbursementDetails(numberOfRepayments,
+ loanApplicationTerms, chargesDueAtTimeOfDisbursement);
+
+ // 4. Determine the total interest owed over the full loan.
+ final Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
+ this.paymentPeriodsInOneYearCalculator, mc);
+
+ // 5. loop over the known set of 'scheduled due dates'
+ LocalDate repaymentPeriodStartDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
+ loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), scheduledDates);
+
+ LocalDate startDate = loanApplicationTerms.getExpectedDisbursementDate();
+ int periodNumber = 1;
+ Money totalCumulativePrincipal = principalDisbursed.zero();
+ Money totalCumulativeInterest = principalDisbursed.zero();
+ Money totalOutstandingInterestPaymentDueToGrace = principalDisbursed.zero();
+ Money outstandingBalance = principalDisbursed;
+ for (LocalDate scheduledDueDate : scheduledDates) {
+
+ final int daysInPeriod = Days.daysBetween(startDate, scheduledDueDate).getDays();
+
+ double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
+ .calculatePortionOfRepaymentPeriodInterestChargingGrace(repaymentPeriodStartDate, scheduledDueDate,
+ loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(),
+ loanApplicationTerms.getRepaymentEvery());
+
+ // 5 determine principal,interest of repayment period
+ PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
+ this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
+ totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, daysInPeriod,
+ outstandingBalance, loanApplicationTerms, periodNumber, mc);
+
+ // update cumulative fields for principal & interest
+ totalCumulativePrincipal = totalCumulativePrincipal.plus(principalInterestForThisPeriod.principal());
+ totalCumulativeInterest = totalCumulativeInterest.plus(principalInterestForThisPeriod.interest());
+ totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
+
+ // 6. update outstandingLoanBlance using correct 'principalDue'
+ outstandingBalance = outstandingBalance.minus(principalInterestForThisPeriod.principal());
+
+ // 7. determine fees and penalties
+ final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, currency);
+ final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, currency);
+
+ // 8. sum up real totalInstallmentDue from components
+ final Money totalInstallmentDue = principalInterestForThisPeriod.principal() //
+ .plus(principalInterestForThisPeriod.interest()) //
+ .plus(feeChargesForInstallment) //
+ .plus(penaltyChargesForInstallment);
+
+ // 9. create repayment period from parts
+ final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, startDate,
+ scheduledDueDate, principalInterestForThisPeriod.principal(), outstandingBalance,
+ principalInterestForThisPeriod.interest(), feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue);
+ periods.add(installment);
+
+ // handle cumulative fields
+ loanTermInDays += daysInPeriod;
+ totalPrincipalExpected = totalPrincipalExpected.add(principalInterestForThisPeriod.principal().getAmount());
+ totalInterestCharged = totalInterestCharged.add(principalInterestForThisPeriod.interest().getAmount());
+ totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
+ totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
+ totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
+ startDate = scheduledDueDate;
+ repaymentPeriodStartDate = startDate;
+
+ periodNumber++;
+ }
+
+ return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
+ totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
+ totalOutstanding);
+ }
+
+ public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
+ double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
+ Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriod, Money outstandingBalance,
+ LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc);
+
+ protected final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
+ return periodNumber == numberOfRepayments;
+ }
+
+ private BigDecimal deriveTotalChargesDueAtTimeOfDisbursement(final Set loanCharges) {
+ BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
+ for (LoanCharge loanCharge : loanCharges) {
+ if (loanCharge.isDueAtDisbursement()) {
+ chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
+ }
+ }
+ return chargesDueAtTimeOfDisbursement;
+ }
+
+ private Collection createNewLoanScheduleListWithDisbursementDetails(final int numberOfRepayments,
+ final LoanApplicationTerms loanApplicationTerms, final BigDecimal chargesDueAtTimeOfDisbursement) {
+
+ final Collection periods = new ArrayList(numberOfRepayments + 1);
+
+ final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
+ loanApplicationTerms, chargesDueAtTimeOfDisbursement);
+ periods.add(disbursementPeriod);
+
+ return periods;
+ }
+
+ private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges,
+ final MonetaryCurrency monetaryCurrency) {
+
+ Money cumulative = Money.zero(monetaryCurrency);
+
+ for (LoanCharge loanCharge : loanCharges) {
+ if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
+ cumulative = cumulative.plus(loanCharge.amount());
+ }
+ }
+
+ return cumulative;
+ }
+
+ private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
+ final Set loanCharges, final MonetaryCurrency monetaryCurrency) {
+
+ Money cumulative = Money.zero(monetaryCurrency);
+
+ for (LoanCharge loanCharge : loanCharges) {
+ if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
+ cumulative = cumulative.plus(loanCharge.amount());
+ }
+ }
+
+ return cumulative;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGenerator.java
deleted file mode 100644
index 4a9f11b0e..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGenerator.java
+++ /dev/null
@@ -1,23 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Set;
-
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-
-public interface AmortizationLoanScheduleGenerator {
-
- LoanScheduleData generate(ApplicationCurrency applicationCurrency, LoanProductRelatedDetail loanScheduleInfo,
- LocalDate disbursementDate, LocalDate interestCalculatedFrom, BigDecimal periodInterestRateForRepaymentPeriod,
- LocalDate idealDisbursementDateBasedOnFirstRepaymentDate, List scheduledDates, Set loanCharges);
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGeneratorFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGeneratorFactory.java
deleted file mode 100644
index f2030fd0b..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AmortizationLoanScheduleGeneratorFactory.java
+++ /dev/null
@@ -1,31 +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.loanschedule.domain;
-
-import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
-
-public class AmortizationLoanScheduleGeneratorFactory {
-
- public AmortizationLoanScheduleGenerator createGenerator(final AmortizationMethod amortizationMethod) {
-
- AmortizationLoanScheduleGenerator generator = new EqualInstallmentsAmortizationLoanScheduleGenerator();
-
- switch (amortizationMethod) {
- case INVALID:
- generator = new EqualInstallmentsAmortizationLoanScheduleGenerator();
- break;
- case EQUAL_INSTALLMENTS:
- generator = new EqualInstallmentsAmortizationLoanScheduleGenerator();
- break;
- case EQUAL_PRINCIPAL:
- generator = new EqualPrincipalAmortizationLoanScheduleGenerator();
- break;
- }
-
- return generator;
- }
-
-}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
new file mode 100644
index 000000000..a6b9e95ac
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
@@ -0,0 +1,67 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.MathContext;
+
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
+
+/**
+ *
+ * Declining balance can be amortized (see {@link AmortizationMethod}) in two
+ * ways at present:
+ *
+ * - Equal principal payments
+ * - Equal installment payments
+ *
+ *
+ *
+ *
+ * When amortized using equal principal payments, the principal
+ * component of each installment is fixed and interest due is
+ * calculated from the outstanding principal balance resulting in a
+ * different total payment due for each installment.
+ *
+ *
+ *
+ * When amortized using equal installments, the total payment due
+ * for each installment is fixed and is calculated using the excel like
+ * pmt function. The interest due is calculated from the
+ * outstanding principal balance which results in a principal
+ * component that is total payment due minus interest due.
+ *
+ */
+public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanScheduleGenerator {
+
+ @Override
+ public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
+ final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
+ @SuppressWarnings("unused") final Money totalCumulativeInterest,
+ @SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
+ final int daysInPeriod, final Money outstandingBalance,
+ final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc) {
+
+ PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
+ interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriod,
+ outstandingBalance);
+
+ Money interestForThisInstallment = result.interest();
+
+ Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriod,
+ outstandingBalance, periodNumber, mc);
+
+ // update cumulative fields for principal & interest
+ Money interestBroughtFowardDueToGrace = result.interestPaymentDueToGrace();
+ Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment);
+
+ // adjust if needed
+ principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
+ totalCumulativePrincipalToDate, periodNumber);
+
+ return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtFowardDueToGrace);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java
deleted file mode 100644
index f23e11a64..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DecliningBalanceMethodLoanScheduleGenerator.java
+++ /dev/null
@@ -1,74 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Set;
-
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
-
-/**
- *
- * Declining balance can be amortized (see {@link AmortizationMethod}) in two ways
- * at present:
- *
- * - Equal principal payments
- * - Equal installment payments
- *
- *
- *
- *
- * When amortized using equal principal payments, the principal
- * component of each installment is fixed and interest due is
- * calculated from the outstanding principal balance resulting in a
- * different total payment due for each installment.
- *
- *
- *
- * When amortized using equal installments, the total payment due
- * for each installment is fixed and is calculated using the excel like
- * pmt function. The interest due is calculated from the
- * outstanding principal balance which results in a principal
- * component that is total payment due minus interest due.
- *
- */
-public class DecliningBalanceMethodLoanScheduleGenerator implements LoanScheduleGenerator {
-
- private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
- private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
- private final AmortizationLoanScheduleGeneratorFactory amortizationLoanScheduleGeneratorFactory = new AmortizationLoanScheduleGeneratorFactory();
-
- @Override
- public LoanScheduleData generate(final ApplicationCurrency applicationCurrency, final LoanProductRelatedDetail loanScheduleInfo,
- final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate,
- final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom, final Set loanCharges) {
-
- // 1. generate valid set of 'due dates' based on some of the 'loan
- // attributes'
- final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
-
- final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate = this.scheduledDateGenerator
- .idealDisbursementDateBasedOnFirstRepaymentDate(loanScheduleInfo, scheduledDates);
-
- // 2. determine the 'periodic' interest rate based on the 'repayment
- // periods' so we can use
- final BigDecimal periodInterestRateForRepaymentPeriod = this.periodicInterestRateCalculator.calculateFrom(loanScheduleInfo);
-
- // Determine with 'amortisation' approach to use
- final AmortizationLoanScheduleGenerator generator = this.amortizationLoanScheduleGeneratorFactory.createGenerator(loanScheduleInfo
- .getAmortizationMethod());
-
- return generator.generate(applicationCurrency, loanScheduleInfo, disbursementDate, interestCalculatedFrom,
- periodInterestRateForRepaymentPeriod, idealDisbursementDateBasedOnFirstRepaymentDate, scheduledDates, loanCharges);
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
index 8bfe8c39a..875fdb0b6 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultLoanScheduleGeneratorFactory.java
@@ -18,10 +18,10 @@ public class DefaultLoanScheduleGeneratorFactory implements LoanScheduleGenerato
switch (interestMethod) {
case FLAT:
- loanScheduleGenerator = new FlatMethodLoanScheduleGenerator();
+ loanScheduleGenerator = new FlatInterestLoanScheduleGenerator();
break;
case DECLINING_BALANCE:
- loanScheduleGenerator = new DecliningBalanceMethodLoanScheduleGenerator();
+ loanScheduleGenerator = new DecliningBalanceInterestLoanScheduleGenerator();
break;
case INVALID:
break;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultPaymentPeriodsInOneYearCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultPaymentPeriodsInOneYearCalculator.java
index 476450dd8..914697e9f 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultPaymentPeriodsInOneYearCalculator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultPaymentPeriodsInOneYearCalculator.java
@@ -5,98 +5,80 @@
*/
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
-import java.util.List;
-
import org.joda.time.Days;
import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
-public class DefaultPaymentPeriodsInOneYearCalculator implements
- PaymentPeriodsInOneYearCalculator {
+public class DefaultPaymentPeriodsInOneYearCalculator implements PaymentPeriodsInOneYearCalculator {
- @Override
- public Integer calculate(final PeriodFrequencyType repaymentFrequencyType) {
+ @Override
+ public Integer calculate(final PeriodFrequencyType repaymentFrequencyType) {
- Integer paymentPeriodsInOneYear = Integer.valueOf(0);
- switch (repaymentFrequencyType) {
- case DAYS:
- paymentPeriodsInOneYear = Integer.valueOf(365);
- break;
- case WEEKS:
- paymentPeriodsInOneYear = Integer.valueOf(52);
- break;
- case MONTHS:
- paymentPeriodsInOneYear = Integer.valueOf(12);
- break;
- case YEARS:
- paymentPeriodsInOneYear = Integer.valueOf(1);
- break;
- case INVALID:
- paymentPeriodsInOneYear = Integer.valueOf(0);
- break;
- }
- return paymentPeriodsInOneYear;
- }
+ Integer paymentPeriodsInOneYear = Integer.valueOf(0);
+ switch (repaymentFrequencyType) {
+ case DAYS:
+ paymentPeriodsInOneYear = Integer.valueOf(365);
+ break;
+ case WEEKS:
+ paymentPeriodsInOneYear = Integer.valueOf(52);
+ break;
+ case MONTHS:
+ paymentPeriodsInOneYear = Integer.valueOf(12);
+ break;
+ case YEARS:
+ paymentPeriodsInOneYear = Integer.valueOf(1);
+ break;
+ case INVALID:
+ paymentPeriodsInOneYear = Integer.valueOf(0);
+ break;
+ }
+ return paymentPeriodsInOneYear;
+ }
- /**
- * calculates the number of grace repayment periods and can be a fraction of a repayment period.
- */
- @Override
- public double calculateRepaymentPeriodAsAFractionOfDays(
- PeriodFrequencyType repaymentPeriodFrequencyType,
- Integer every, LocalDate interestCalculatedFrom,
- List scheduledDates, LocalDate disbursementDate) {
-
- Double periodFraction = Double.valueOf("0");
-
- if (interestCalculatedFrom != null && !interestCalculatedFrom.isBefore(disbursementDate)) {
- Integer repaymentPeriodIndex = 0;
- LocalDate lastRepaymentPeriod = disbursementDate;
- for (LocalDate repaymentPeriodDueDate : scheduledDates) {
- if (interestCalculatedFrom.isBefore(repaymentPeriodDueDate)) {
- // fraction is
- int numberOfDaysInterestCalculationGraceInPeriod = Days
- .daysBetween(
- lastRepaymentPeriod.toDateMidnight()
- .toDateTime(),
- interestCalculatedFrom.toDateMidnight()
- .toDateTime()).getDays();
- periodFraction = calculateRepaymentPeriodFraction(
- repaymentPeriodFrequencyType, every,
- numberOfDaysInterestCalculationGraceInPeriod);
- periodFraction = periodFraction
- + repaymentPeriodIndex.doubleValue();
- break;
- }
- repaymentPeriodIndex++;
- lastRepaymentPeriod = repaymentPeriodDueDate;
- }
- }
-
- return periodFraction;
- }
-
- private double calculateRepaymentPeriodFraction(PeriodFrequencyType repaymentPeriodFrequencyType, Integer every, Integer numberOfDaysInterestCalculationGrace) {
-
- Double fraction = Double.valueOf("0");
- switch (repaymentPeriodFrequencyType) {
- case DAYS:
- fraction = numberOfDaysInterestCalculationGrace.doubleValue() * every.doubleValue();
- break;
- case WEEKS:
- fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("7.0") * every.doubleValue());
- break;
- case MONTHS:
- fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("30.0") * every.doubleValue());
- break;
- case YEARS:
- fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("365.0") * every.doubleValue());
- break;
- case INVALID:
- fraction = Double.valueOf("0");
- break;
- }
- return fraction;
- }
+ @Override
+ public double calculatePortionOfRepaymentPeriodInterestChargingGrace(final LocalDate repaymentPeriodStartDate,
+ final LocalDate scheduledDueDate, final LocalDate interestChargedFromLocalDate,
+ final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer repaidEvery) {
-}
+ Double periodFraction = Double.valueOf("0.0");
+
+ LocalDateInterval repaymentPeriod = new LocalDateInterval(repaymentPeriodStartDate, scheduledDueDate);
+
+ if (interestChargedFromLocalDate != null && repaymentPeriod.fallsBefore(interestChargedFromLocalDate.plusDays(1))) {
+ periodFraction = Double.valueOf("1.0");
+ } else if (interestChargedFromLocalDate != null && repaymentPeriod.contains(interestChargedFromLocalDate)) {
+
+ int numberOfDaysInterestCalculationGraceInPeriod = Days.daysBetween(repaymentPeriodStartDate, interestChargedFromLocalDate)
+ .getDays();
+ periodFraction = calculateRepaymentPeriodFraction(repaymentPeriodFrequencyType, repaidEvery,
+ numberOfDaysInterestCalculationGraceInPeriod);
+ }
+
+ return periodFraction;
+ }
+
+ private double calculateRepaymentPeriodFraction(final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer every,
+ final Integer numberOfDaysInterestCalculationGrace) {
+
+ Double fraction = Double.valueOf("0");
+ switch (repaymentPeriodFrequencyType) {
+ case DAYS:
+ fraction = numberOfDaysInterestCalculationGrace.doubleValue() * every.doubleValue();
+ break;
+ case WEEKS:
+ fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("7.0") * every.doubleValue());
+ break;
+ case MONTHS:
+ fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("30.0") * every.doubleValue());
+ break;
+ case YEARS:
+ fraction = numberOfDaysInterestCalculationGrace.doubleValue() / (Double.valueOf("365.0") * every.doubleValue());
+ break;
+ case INVALID:
+ fraction = Double.valueOf("0");
+ break;
+ }
+ return fraction;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
index 95cb12b45..575ee2c00 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
@@ -9,46 +9,47 @@ import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
+import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
@Override
- public List generate(final LoanProductMinimumRepaymentScheduleRelatedDetail loanScheduleDateInfo, final LocalDate disbursementDate, final LocalDate firstRepaymentPeriodDate) {
+ public List generate(final LoanApplicationTerms loanApplicationTerms) {
- List dueRepaymentPeriodDates = new ArrayList(loanScheduleDateInfo.getNumberOfRepayments());
+ final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
- LocalDate startDate = disbursementDate;
+ final List dueRepaymentPeriodDates = new ArrayList(numberOfRepayments);
- for (int repaymentPeriod = 1; repaymentPeriod <= loanScheduleDateInfo.getNumberOfRepayments(); repaymentPeriod++) {
+ LocalDate startDate = loanApplicationTerms.getExpectedDisbursementDate();
+ final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getRepaymentsStartingFromLocalDate();
+
+ for (int repaymentPeriod = 1; repaymentPeriod <= numberOfRepayments; repaymentPeriod++) {
LocalDate dueRepaymentPeriodDate = startDate;
-
- if (repaymentPeriod == 1 && firstRepaymentPeriodDate != null) {
- dueRepaymentPeriodDate = firstRepaymentPeriodDate;
- } else {
- switch (loanScheduleDateInfo.getRepaymentPeriodFrequencyType()) {
- case DAYS:
- dueRepaymentPeriodDate = startDate.plusDays(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case WEEKS:
- dueRepaymentPeriodDate = startDate.plusWeeks(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case MONTHS:
- dueRepaymentPeriodDate = startDate.plusMonths(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case YEARS:
- dueRepaymentPeriodDate = startDate.plusYears(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case INVALID:
- break;
- }
- }
+
+ if (repaymentPeriod == 1 && firstRepaymentPeriodDate != null) {
+ dueRepaymentPeriodDate = firstRepaymentPeriodDate;
+ } else {
+
+ final int repaidEvery = loanApplicationTerms.getRepaymentEvery();
+
+ switch (loanApplicationTerms.getRepaymentPeriodFrequencyType()) {
+ case DAYS:
+ dueRepaymentPeriodDate = startDate.plusDays(repaidEvery);
+ break;
+ case WEEKS:
+ dueRepaymentPeriodDate = startDate.plusWeeks(repaidEvery);
+ break;
+ case MONTHS:
+ dueRepaymentPeriodDate = startDate.plusMonths(repaidEvery);
+ break;
+ case YEARS:
+ dueRepaymentPeriodDate = startDate.plusYears(repaidEvery);
+ break;
+ case INVALID:
+ break;
+ }
+ }
dueRepaymentPeriodDates.add(dueRepaymentPeriodDate);
startDate = dueRepaymentPeriodDate;
@@ -57,35 +58,31 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
return dueRepaymentPeriodDates;
}
- @Override
- public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(
- LoanProductRelatedDetail loanScheduleDateInfo,
- List scheduledDates) {
-
- LocalDate firstRepaymentDate = scheduledDates.get(0);
-
- LocalDate idealDisbursementDate = null;
-
- switch (loanScheduleDateInfo.getRepaymentPeriodFrequencyType()) {
- case DAYS:
- idealDisbursementDate = firstRepaymentDate.minusDays(loanScheduleDateInfo.getRepayEvery());
- break;
- case WEEKS:
- idealDisbursementDate = firstRepaymentDate.minusWeeks(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case MONTHS:
- idealDisbursementDate = firstRepaymentDate.minusMonths(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case YEARS:
- idealDisbursementDate = firstRepaymentDate.minusYears(loanScheduleDateInfo
- .getRepayEvery());
- break;
- case INVALID:
- break;
- }
-
- return idealDisbursementDate;
- }
+ @Override
+ public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(final PeriodFrequencyType repaymentPeriodFrequencyType,
+ final int repaidEvery, final List scheduledDates) {
+
+ LocalDate firstRepaymentDate = scheduledDates.get(0);
+
+ LocalDate idealDisbursementDate = null;
+
+ switch (repaymentPeriodFrequencyType) {
+ case DAYS:
+ idealDisbursementDate = firstRepaymentDate.minusDays(repaidEvery);
+ break;
+ case WEEKS:
+ idealDisbursementDate = firstRepaymentDate.minusWeeks(repaidEvery);
+ break;
+ case MONTHS:
+ idealDisbursementDate = firstRepaymentDate.minusMonths(repaidEvery);
+ break;
+ case YEARS:
+ idealDisbursementDate = firstRepaymentDate.minusYears(repaidEvery);
+ break;
+ case INVALID:
+ break;
+ }
+
+ return idealDisbursementDate;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java
deleted file mode 100644
index 2114147fd..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualInstallmentsAmortizationLoanScheduleGenerator.java
+++ /dev/null
@@ -1,190 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import org.joda.time.Days;
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.data.CurrencyData;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.organisation.monetary.domain.Money;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-
-public class EqualInstallmentsAmortizationLoanScheduleGenerator implements AmortizationLoanScheduleGenerator {
-
- private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
- private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
- private final PmtCalculator pmtCalculator = new PmtCalculator();
-
- @Override
- public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo,
- final LocalDate disbursementDate, final LocalDate interestCalculatedFrom,
- final BigDecimal periodInterestRateForRepaymentPeriod, final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
- final List scheduledDates, final Set loanCharges) {
-
- final Collection periods = new ArrayList();
-
- // determine 'total payment' for each repayment based on pmt function
- // (and hence the total due overall)
- final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
- final Money totalDuePerInstallment = this.pmtCalculator.calculatePaymentForOnePeriodFrom(loanScheduleInfo,
- periodInterestRateForRepaymentPeriod, monetaryCurrency);
- final Money totalRepaymentDueForLoanTerm = this.pmtCalculator.calculateTotalRepaymentFrom(loanScheduleInfo,
- periodInterestRateForRepaymentPeriod, monetaryCurrency);
-
- Money totalInterestDue = totalRepaymentDueForLoanTerm.minus(loanScheduleInfo.getPrincipal());
- 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());
- }
- }
-
- // 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 = chargesDueAtTimeOfDisbursement;
- 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, scheduledDueDate).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);
- totalInterestDue = totalInterestDue.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);
- totalInterestDue = totalInterestDue.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());
- }
-
- final Money interestDifference = totalInterest.minus(totalInterestDue);
- if (interestDifference.isLessThanZero()) {
- interestForInstallment = interestForInstallment.plus(interestDifference.abs());
- } else if (interestDifference.isGreaterThanZero()) {
- interestForInstallment = interestForInstallment.minus(interestDifference.abs());
- }
- }
-
- outstandingBalance = outstandingBalance.minus(principalForInstallment);
-
- final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
- final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges,
- monetaryCurrency);
- final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment)
- .plus(penaltyChargesForInstallment);
-
- 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());
- totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
- totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.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);
- }
-
- private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges,
- final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-
- private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
- final Set loanCharges, final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java
deleted file mode 100644
index 6766ce2f3..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/EqualPrincipalAmortizationLoanScheduleGenerator.java
+++ /dev/null
@@ -1,178 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import org.joda.time.Days;
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.data.CurrencyData;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.organisation.monetary.domain.Money;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
-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 scheduledDates, final Set loanCharges) {
-
- final Collection periods = new ArrayList();
-
- // 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);
-
- 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());
- }
- }
-
- // 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 = chargesDueAtTimeOfDisbursement;
- 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);
-
- final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
- final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges,
- monetaryCurrency);
-
- final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment)
- .plus(penaltyChargesForInstallment);
-
- 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());
- totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
- totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.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);
- }
-
- private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges,
- final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-
- private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
- final Set loanCharges, final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FinanicalFunctions.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FinanicalFunctions.java
new file mode 100644
index 000000000..bad420c4f
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FinanicalFunctions.java
@@ -0,0 +1,106 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
+
+public class FinanicalFunctions {
+
+ @Deprecated
+ public Money calculatePaymentForOnePeriodFrom(final LoanProductRelatedDetail loanScheduleInfo,
+ final BigDecimal periodInterestRateForRepaymentPeriod, final MonetaryCurrency monetaryCurrency) {
+ double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
+
+ double futureValue = 0;
+ double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
+ double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
+
+ // work out the total payment (principal + interest components) for each
+ // installment due.
+ // use the period type and interest as expressed e.g. 2% per month or
+ // 24% per year even is 'daily' interest calculation is selected
+ double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
+
+ return Money.of(monetaryCurrency, BigDecimal.valueOf(paymentPerInstallment));
+ }
+
+ @Deprecated
+ public Money calculateTotalRepaymentFrom(final LoanProductRelatedDetail loanScheduleInfo,
+ final BigDecimal periodInterestRateForRepaymentPeriod, final MonetaryCurrency monetaryCurrency) {
+ double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
+
+ double futureValue = 0;
+ double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
+ double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
+
+ // work out the total payment (principal + interest components) for each
+ // installment due.
+ // use the period type and interest as expressed e.g. 2% per month or
+ // 24% per year even is 'daily' interest calculation is selected
+ double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
+
+ double totalRepayment = paymentPerInstallment * loanScheduleInfo.getNumberOfRepayments();
+
+ return Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
+ }
+
+ @Deprecated
+ public Money calculateTotalRepaymentFrom(final Money principal, final Integer loanTermFrequency,
+ final BigDecimal periodInterestRateForLoanTermPeriod, final MonetaryCurrency monetaryCurrency) {
+
+ double interestRateFraction = periodInterestRateForLoanTermPeriod.doubleValue();
+
+ double futureValue = 0;
+ double numberOfPeriods = loanTermFrequency.doubleValue();
+ double principalAmount = principal.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
+
+ // work out the total payment (principal + interest components) for each
+ // installment due.
+ // use the period type and interest as expressed e.g. 2% per month or
+ // 24% per year even is 'daily' interest calculation is selected
+ double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principalAmount, futureValue, false);
+
+ double totalRepayment = paymentPerInstallment * loanTermFrequency;
+
+ return Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
+ }
+
+ /**
+ * PMT calculates a fixed monthly payment to be paid by borrower every
+ * 'period' to ensure loan is paid off in full (with interest).
+ *
+ * This monthly payment c depends upon the monthly interest rate r
+ * (expressed as a fraction, not a percentage, i.e., divide the quoted
+ * yearly percentage rate by 100 and by 12 to obtain the monthly interest
+ * rate), the number of monthly payments N called the loan's term, and the
+ * amount borrowed P known as the loan's principal; c is given by the
+ * formula:
+ *
+ * c = (r / (1 - (1 + r)^-N))P
+ *
+ * @param interestRateFraction
+ * @param numberOfPayments
+ * @param principal
+ * @param futureValue
+ * @param type
+ */
+ public static double pmt(final double interestRateFraction, final double numberOfPayments, final double principal,
+ final double futureValue, final boolean type) {
+ double payment = 0;
+ if (interestRateFraction == 0) {
+ payment = -1 * (futureValue + principal) / numberOfPayments;
+ } else {
+ double r1 = interestRateFraction + 1;
+ payment = (futureValue + principal * Math.pow(r1, numberOfPayments)) * interestRateFraction
+ / ((type ? r1 : 1) * (1 - Math.pow(r1, numberOfPayments)));
+ }
+ return payment;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
new file mode 100644
index 000000000..50ed49518
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
@@ -0,0 +1,43 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.MathContext;
+
+import org.mifosplatform.organisation.monetary.domain.Money;
+
+public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGenerator {
+
+ @Override
+ public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
+ final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
+ final Money totalCumulativeInterest, final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
+ final int daysInPeriod, final Money outstandingBalance,
+ final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc) {
+
+ Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, daysInPeriod,
+ outstandingBalance, periodNumber, mc);
+
+ PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
+ interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestPaymentDueToGrace, daysInPeriod,
+ outstandingBalance);
+ Money interestForThisInstallment = result.interest();
+
+ // update cumulative fields for principal & interest
+ Money interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
+ Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment);
+ Money totalCumulativeInterestToDate = totalCumulativeInterest.plus(interestForThisInstallment);
+
+ // adjust if needed
+ principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
+ totalCumulativePrincipalToDate, periodNumber);
+
+ interestForThisInstallment = loanApplicationTerms.adjustInterestIfLastRepaymentPeriod(interestForThisInstallment,
+ totalCumulativeInterestToDate, totalInterestDueForLoan, periodNumber);
+
+ return new PrincipalInterest(principalForThisInstallment, interestForThisInstallment, interestBroughtForwardDueToGrace);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
deleted file mode 100644
index 9244aea8c..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/FlatMethodLoanScheduleGenerator.java
+++ /dev/null
@@ -1,178 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import org.joda.time.Days;
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.data.CurrencyData;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.organisation.monetary.domain.Money;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
-
-public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
-
- private final ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
- private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
-
- @Override
- public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo,
- final Integer loanTermFrequency, final PeriodFrequencyType loanTermFrequencyType, final LocalDate disbursementDate,
- final LocalDate firstRepaymentDate, final LocalDate interestCalculatedFrom, final Set loanCharges) {
-
- final Collection periods = new ArrayList();
-
- final List scheduledDates = this.scheduledDateGenerator.generate(loanScheduleInfo, disbursementDate, firstRepaymentDate);
-
- MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
-
- BigDecimal loanTermPeriodsInYear = BigDecimal.valueOf(this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType));
- BigDecimal interestRateForLoanTerm = loanScheduleInfo.getAnnualNominalInterestRate().divide(loanTermPeriodsInYear, mc)
- .divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc).multiply(BigDecimal.valueOf(loanTermFrequency));
-
- final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
- Money totalInterestForLoanTerm = loanScheduleInfo.getPrincipal().multiplyRetainScale(interestRateForLoanTerm,
- RoundingMode.HALF_EVEN);
-
- Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(loanScheduleInfo.getNumberOfRepayments()),
- RoundingMode.HALF_EVEN);
-
- Money principalPerInstallment = loanScheduleInfo.getPrincipal().dividedBy(loanScheduleInfo.getNumberOfRepayments(),
- RoundingMode.HALF_EVEN);
-
- Money outstandingBalance = loanScheduleInfo.getPrincipal();
- Money principalDisbursed = loanScheduleInfo.getPrincipal();
- Money totalPrincipal = Money.zero(outstandingBalance.getCurrency());
- Money totalInterest = Money.zero(outstandingBalance.getCurrency());
-
- BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueAtDisbursement()) {
- chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
- }
- }
-
- // 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 = chargesDueAtTimeOfDisbursement;
- 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) {
- totalPrincipal = totalPrincipal.plus(principalPerInstallment);
- totalInterest = totalInterest.plus(interestPerInstallment);
-
- // number of days from startDate to this scheduledDate
- int daysInPeriod = Days.daysBetween(startDate.toDateMidnight().toDateTime(), scheduledDueDate.toDateMidnight().toDateTime())
- .getDays();
-
- if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
- final Money difference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
- if (difference.isLessThanZero()) {
- principalPerInstallment = principalPerInstallment.plus(difference.abs());
- } else if (difference.isGreaterThanZero()) {
- principalPerInstallment = principalPerInstallment.minus(difference.abs());
- }
-
- final Money interestDifference = totalInterest.minus(totalInterestForLoanTerm);
- if (interestDifference.isLessThanZero()) {
- interestPerInstallment = interestPerInstallment.plus(interestDifference.abs());
- } else if (interestDifference.isGreaterThanZero()) {
- interestPerInstallment = interestPerInstallment.minus(interestDifference.abs());
- }
- }
-
- outstandingBalance = outstandingBalance.minus(principalPerInstallment);
-
- final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
- final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges,
- monetaryCurrency);
-
- final Money totalInstallmentDue = principalPerInstallment //
- .plus(interestPerInstallment) //
- .plus(feeChargesForInstallment) //
- .plus(penaltyChargesForInstallment);
-
- LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate,
- principalPerInstallment.getAmount(), outstandingBalance.getAmount(), interestPerInstallment.getAmount(),
- feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount());
-
- periods.add(installment);
-
- // handle cumulative fields
- loanTermInDays += daysInPeriod;
- totalPrincipalExpected = totalPrincipalExpected.add(principalPerInstallment.getAmount());
- totalInterestCharged = totalInterestCharged.add(interestPerInstallment.getAmount());
- totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
- totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.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);
- }
-
- private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges,
- final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-
- private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
- final Set loanCharges, final MonetaryCurrency monetaryCurrency) {
-
- Money cumulative = Money.zero(monetaryCurrency);
-
- for (LoanCharge loanCharge : loanCharges) {
- if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
- cumulative = cumulative.plus(loanCharge.amount());
- }
- }
-
- return cumulative;
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
new file mode 100644
index 000000000..f1711f334
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -0,0 +1,691 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.Date;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
+import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
+
+public final class LoanApplicationTerms {
+
+ private final ApplicationCurrency currency;
+ private final Integer loanTermFrequency;
+ private final PeriodFrequencyType loanTermPeriodFrequencyType;
+ private final Integer numberOfRepayments;
+ private final Integer repaymentEvery;
+ private final PeriodFrequencyType repaymentPeriodFrequencyType;
+ private final AmortizationMethod amortizationMethod;
+
+ private final InterestMethod interestMethod;
+ private final BigDecimal interestRatePerPeriod;
+ private final PeriodFrequencyType interestRatePeriodFrequencyType;
+ private final BigDecimal annualNominalInterestRate;
+ private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
+
+ private final Money principal;
+ private final LocalDate expectedDisbursementDate;
+ private final LocalDate repaymentsStartingFromDate;
+
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the principal component of a
+ * loans repayment period (installment).
+ */
+ private final Integer principalGrace;
+
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the payment of interest in a
+ * loans repayment period (installment).
+ *
+ * Note: Interest is still calculated taking into account the full
+ * loan term, the interest is simply offset to a later period.
+ */
+ private final Integer interestPaymentGrace;
+
+ /**
+ * Integer representing the number of 'repayment frequencies' or
+ * installments where 'grace' should apply to the charging of interest in a
+ * loans repayment period (installment).
+ *
+ * Note: The loan is interest-free for the period of time
+ * indicated.
+ */
+ private final Integer interestChargingGrace;
+
+ /**
+ * Legacy method of support 'grace' on the charging of interest on a loan.
+ *
+ *
+ * For the typical structured loan, its reasonable to use an integer to
+ * indicate the number of 'repayment frequency' periods the 'grace' should
+ * apply to but for slightly irregular loans where the period between
+ * disbursement and the date of the 'first repayment period' isnt doest
+ * match the 'repayment frequency' but can be less (15days instead of 1
+ * month) or more (6 weeks instead of 1 month) - The idea was to use a date
+ * to indicate from whence interest should be charged.
+ *
+ */
+ private final LocalDate interestChargedFromDate;
+ private final Money inArrearsTolerance;
+
+ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
+ final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
+ final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod,
+ final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualNominalInterestRate,
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Money principalMoney,
+ final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, final Integer graceOnPrincipalPayment,
+ final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate,
+ final Money inArrearsTolerance) {
+
+ return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
+ repaymentPeriodFrequencyType, amortizationMethod, interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType,
+ annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney, expectedDisbursementDate,
+ repaymentsStartingFromDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
+ interestChargedFromDate, inArrearsTolerance);
+ }
+
+ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, final LocalDate expectedDisbursementDate,
+ final LocalDate repaymentsStartingFromDate, final Money inArrearsTolerance,
+ final LoanProductRelatedDetail loanProductRelatedDetail) {
+
+ final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
+ final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
+ final PeriodFrequencyType repaymentPeriodFrequencyType = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
+ final AmortizationMethod amortizationMethod = loanProductRelatedDetail.getAmortizationMethod();
+ final InterestMethod interestMethod = loanProductRelatedDetail.getInterestMethod();
+ final BigDecimal interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
+ final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType();
+ final BigDecimal annualNominalInterestRate = loanProductRelatedDetail.getAnnualNominalInterestRate();
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
+ .getInterestCalculationPeriodMethod();
+ final Money principalMoney = loanProductRelatedDetail.getPrincipal();
+
+ //
+ final Integer graceOnPrincipalPayment = 0;
+ final Integer graceOnInterestPayment = 0;
+ final Integer graceOnInterestCharged = 0;
+ final LocalDate interestChargedFromDate = null;
+
+ return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
+ repaymentEvery, repaymentPeriodFrequencyType, amortizationMethod, interestMethod, interestRatePerPeriod,
+ interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney,
+ expectedDisbursementDate, repaymentsStartingFromDate, graceOnPrincipalPayment, graceOnInterestPayment,
+ graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance);
+ }
+
+ private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
+ final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
+ final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
+ final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod,
+ final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualNominalInterestRate,
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Money principal,
+ final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, final Integer principalGrace,
+ final Integer interestPaymentGrace, final Integer interestChargingGrace, final LocalDate interestChargedFromDate,
+ final Money inArrearsTolerance) {
+ this.currency = currency;
+ this.loanTermFrequency = loanTermFrequency;
+ this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
+ this.numberOfRepayments = numberOfRepayments;
+ this.repaymentEvery = repaymentEvery;
+ this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
+ this.amortizationMethod = amortizationMethod;
+
+ this.interestMethod = interestMethod;
+ this.interestRatePerPeriod = interestRatePerPeriod;
+ this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
+ this.annualNominalInterestRate = annualNominalInterestRate;
+ this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
+
+ this.principal = principal;
+ this.expectedDisbursementDate = expectedDisbursementDate;
+ this.repaymentsStartingFromDate = repaymentsStartingFromDate;
+
+ this.principalGrace = principalGrace;
+ this.interestPaymentGrace = interestPaymentGrace;
+ this.interestChargingGrace = interestChargingGrace;
+ this.interestChargedFromDate = interestChargedFromDate;
+
+ this.inArrearsTolerance = inArrearsTolerance;
+ }
+
+ public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
+ final int periodNumber) {
+
+ Money adjusted = principalForPeriod;
+
+ // adjust cumulative fields for principal & interest if needed
+ if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
+
+ final Money difference = totalCumulativePrincipalToDate.minus(this.principal);
+ if (difference.isLessThanZero()) {
+ adjusted = principalForPeriod.plus(difference.abs());
+ } else if (difference.isGreaterThanZero()) {
+ adjusted = principalForPeriod.minus(difference.abs());
+ }
+ }
+
+ return adjusted;
+ }
+
+ public Money adjustInterestIfLastRepaymentPeriod(final Money interestForThisPeriod, final Money totalCumulativeInterestToDate,
+ final Money totalInterestDueForLoan, final int periodNumber) {
+
+ Money adjusted = interestForThisPeriod;
+
+ if (isLastRepaymentPeriod(this.numberOfRepayments, periodNumber)) {
+
+ final Money interestDifference = totalCumulativeInterestToDate.minus(totalInterestDueForLoan);
+ if (interestDifference.isLessThanZero()) {
+ adjusted = interestForThisPeriod.plus(interestDifference.abs());
+ } else if (interestDifference.isGreaterThanZero()) {
+ adjusted = interestForThisPeriod.minus(interestDifference.abs());
+ }
+ }
+
+ return adjusted;
+ }
+
+ /**
+ * Calculates the total interest to be charged on loan taking into account
+ * grace settings.
+ *
+ */
+ public Money calculateTotalInterestCharged(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
+
+ Money totalInterestCharged = this.principal.zero();
+
+ switch (this.interestMethod) {
+ case FLAT:
+ final Money totalInterestChargedForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
+
+ final Money totalInterestPerInstallment = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
+
+ final Money totalGraceOnInterestCharged = totalInterestPerInstallment.multiplyRetainScale(getInterestChargingGrace(),
+ mc.getRoundingMode());
+
+ totalInterestCharged = totalInterestChargedForLoanTerm.minus(totalGraceOnInterestCharged);
+ break;
+ case DECLINING_BALANCE:
+ final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc);
+
+ final Money totalRepaymentDueForLoanTerm = calculateTotalDueForEqualInstallmentLoan(periodicInterestRateForRepaymentPeriod);
+
+ totalInterestCharged = totalRepaymentDueForLoanTerm.minus(this.principal);
+ break;
+ case INVALID:
+ break;
+ }
+
+ return totalInterestCharged;
+ }
+
+ public Money calculateTotalPrincipalForPeriod(final PaymentPeriodsInOneYearCalculator calculator, final int daysInPeriod,
+ final Money outstandingBalance, final int periodNumber, final MathContext mc) {
+
+ Money principalForInstallment = this.principal.zero();
+
+ switch (this.interestMethod) {
+ case FLAT:
+ principalForInstallment = calculateTotalPrincipalPerPeriodWithoutGrace(mc);
+ if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
+ principalForInstallment = Money.zero(principalForInstallment.getCurrency());
+ }
+ break;
+ case DECLINING_BALANCE:
+ switch (this.amortizationMethod) {
+ case EQUAL_INSTALLMENTS:
+ // equal installments
+ int periodsElapsed = periodNumber - 1;
+ final BigDecimal periodicInterestRateForRepaymentPeriod = periodicInterestRate(calculator, mc);
+
+ final Money totalPmtForThisInstallment = calculateTotalDueForEqualInstallmentRepaymentPeriod(
+ periodicInterestRateForRepaymentPeriod, outstandingBalance, periodsElapsed);
+
+ principalForInstallment = calculatePrincipalDueForInstallment(calculator, mc, periodNumber,
+ totalPmtForThisInstallment, daysInPeriod, outstandingBalance);
+ break;
+ case EQUAL_PRINCIPAL:
+ principalForInstallment = calculateEqualPrincipalDueForInstallment(mc, periodNumber);
+ break;
+ case INVALID:
+ break;
+ }
+ break;
+ case INVALID:
+ break;
+ }
+
+ return principalForInstallment;
+ }
+
+ public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
+ final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
+ final Money cumulatingInterestPaymentDueToGrace, final int daysInPeriod, final Money outstandingBalance) {
+
+ Money interestForInstallment = this.principal.zero();
+ Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
+
+ switch (this.interestMethod) {
+ case FLAT:
+
+ switch (this.amortizationMethod) {
+ case EQUAL_INSTALLMENTS:
+ // average out outstanding interest over remaining
+ // instalments where interest is applicable
+ interestForInstallment = calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(calculator, periodNumber,
+ mc);
+ break;
+ case EQUAL_PRINCIPAL:
+ // interest follows time-value of money and is brought
+ // forward to next applicable interest payment period
+ PrincipalInterest result = calculateTotalFlatInterestForPeriod(calculator, periodNumber, mc,
+ interestBroughtForwardDueToGrace);
+ interestForInstallment = result.interest();
+ interestBroughtForwardDueToGrace = result.interestPaymentDueToGrace();
+ break;
+ case INVALID:
+ break;
+ }
+ break;
+ case DECLINING_BALANCE:
+
+ Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator,
+ mc, daysInPeriod, outstandingBalance);
+
+ Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(calculator,
+ interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriod, outstandingBalance, periodNumber);
+
+ interestForInstallment = interestForThisInstallmentAfterGrace;
+ if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) {
+ interestForInstallment = interestBroughtForwardDueToGrace.plus(interestForThisInstallmentAfterGrace);
+ interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.zero();
+ } else if (isInterestFreeGracePeriod(periodNumber)) {
+ interestForInstallment = interestForInstallment.zero();
+ } else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
+ interestForInstallment = interestForThisInstallmentAfterGrace;
+ } else {
+ interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.plus(interestForThisInstallmentBeforeGrace);
+ }
+ break;
+ case INVALID:
+ break;
+ }
+
+ return new PrincipalInterest(null, interestForInstallment, interestBroughtForwardDueToGrace);
+ }
+
+ private final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
+ return periodNumber == numberOfRepayments;
+ }
+
+ private Money calculateTotalDueForEqualInstallmentLoan(final BigDecimal periodicInterestRate) {
+
+ final int periodsElapsed = 0;
+ final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
+ final double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
+
+ return Money.of(principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
+ }
+
+ /**
+ * general method to calculate totalInterestDue discounting any grace
+ * settings
+ */
+ private Money calculateTotalInterestDueWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
+
+ Money totalInterestDue = this.principal.zero();
+
+ switch (this.interestMethod) {
+ case FLAT:
+ final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
+
+ totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
+ break;
+ case DECLINING_BALANCE:
+ break;
+ case INVALID:
+ break;
+ }
+
+ return totalInterestDue;
+ }
+
+ private BigDecimal calculateFlatInterestRateForLoanTerm(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
+
+ final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0"));
+
+ final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
+ final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
+
+ final BigDecimal loanTermFrequencyBigDecimal = BigDecimal.valueOf(this.loanTermFrequency);
+
+ return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
+ .multiply(loanTermFrequencyBigDecimal);
+ }
+
+ private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
+
+ final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
+
+ return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.numberOfRepayments), mc.getRoundingMode());
+ }
+
+ private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc) {
+ final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
+ return this.principal.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode());
+ }
+
+ private PrincipalInterest calculateTotalFlatInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
+ final int periodNumber, final MathContext mc, final Money cumulatingInterestPaymentDueToGrace) {
+
+ Money interestBroughtForwardDueToGrace = cumulatingInterestPaymentDueToGrace.copy();
+
+ Money interestForInstallment = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
+ if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
+ interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.plus(interestForInstallment);
+ interestForInstallment = interestForInstallment.zero();
+ } else if (isInterestFreeGracePeriod(periodNumber)) {
+ interestForInstallment = interestForInstallment.zero();
+ } else if (isFirstPeriodAfterInterestPaymentGracePeriod(periodNumber)) {
+ interestForInstallment = cumulatingInterestPaymentDueToGrace.plus(interestForInstallment);
+ interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.zero();
+ }
+
+ return new PrincipalInterest(null, interestForInstallment, interestBroughtForwardDueToGrace);
+ }
+
+ /*
+ * calculates the interest that should be due for a given scheduled loan
+ * repayment period. It takes into account GRACE periods and calculates how
+ * much interest is due per period by averaging the number of periods where
+ * interest is due and should be paid against the total known interest that
+ * is due without grace.
+ */
+ private Money calculateTotalFlatInterestForInstallmentAveragingOutGracePeriods(final PaymentPeriodsInOneYearCalculator calculator,
+ final int periodNumber, final MathContext mc) {
+
+ Money interestForInstallment = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
+ if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
+ interestForInstallment = interestForInstallment.zero();
+ } else if (isInterestFreeGracePeriod(periodNumber)) {
+ interestForInstallment = interestForInstallment.zero();
+ } else {
+
+ final Money totalInterestForLoanTerm = calculateTotalInterestDueWithoutGrace(calculator, mc);
+
+ Money interestPerGracePeriod = calculateTotalInterestPerInstallmentWithoutGrace(calculator, mc);
+
+ Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
+ Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
+
+ Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.numberOfRepayments);
+
+ interestForInstallment = realTotalInterestForLoan
+ .dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
+ }
+
+ return interestForInstallment;
+ }
+
+ private BigDecimal periodicInterestRate(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
+
+ final long loanTermPeriodsInOneYear = calculatePeriodsInOneYear(calculator);
+
+ final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0"));
+ final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
+
+ final BigDecimal loanTermFrequencyBigDecimal = BigDecimal.valueOf(this.repaymentEvery);
+
+ return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
+ .multiply(loanTermFrequencyBigDecimal);
+ }
+
+ private long calculatePeriodsInOneYear(final PaymentPeriodsInOneYearCalculator calculator) {
+ return calculator.calculate(this.repaymentPeriodFrequencyType).longValue();
+ }
+
+ private int calculateNumberOfRepaymentsWithPrincipalPayment() {
+ return this.numberOfRepayments - getPrincipalGrace();
+ }
+
+ private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(final Integer totalNumberOfRepaymentPeriods) {
+ return totalNumberOfRepaymentPeriods - Math.max(getInterestChargingGrace(), getInterestPaymentGrace());
+ }
+
+ private Integer calculateNumberOfPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods) {
+ return totalNumberOfRepaymentPeriods - getPrincipalGrace();
+ }
+
+ private boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) {
+ return periodNumber > 0 && periodNumber <= getPrincipalGrace();
+ }
+
+ private boolean isInterestPaymentGraceApplicableForThisPeriod(final int periodNumber) {
+ return periodNumber > 0 && periodNumber <= getInterestPaymentGrace();
+ }
+
+ private boolean isFirstPeriodAfterInterestPaymentGracePeriod(final int periodNumber) {
+ return periodNumber > 0 && periodNumber == getInterestPaymentGrace() + 1;
+ }
+
+ private boolean isInterestFreeGracePeriod(final int periodNumber) {
+ return periodNumber > 0 && periodNumber <= getInterestChargingGrace();
+ }
+
+ private Integer getPrincipalGrace() {
+ Integer graceOnPrincipalPayments = Integer.valueOf(0);
+ if (this.principalGrace != null) {
+ graceOnPrincipalPayments = this.principalGrace;
+ }
+ return graceOnPrincipalPayments;
+ }
+
+ private Integer getInterestPaymentGrace() {
+ Integer graceOnInterestPayments = Integer.valueOf(0);
+ if (this.interestPaymentGrace != null) {
+ graceOnInterestPayments = this.interestPaymentGrace;
+ }
+ return graceOnInterestPayments;
+ }
+
+ private Integer getInterestChargingGrace() {
+ Integer graceOnInterestCharged = Integer.valueOf(0);
+ if (this.interestChargingGrace != null) {
+ graceOnInterestCharged = this.interestChargingGrace;
+ }
+ return graceOnInterestCharged;
+ }
+
+ private double paymentPerPeriod(final BigDecimal periodicInterestRate, final Money balance, final int periodsElapsed) {
+ final double futureValue = 0;
+ final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
+
+ final Integer periodsRemaining = this.numberOfRepayments - periodsElapsed;
+
+ return FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(), principalDouble, futureValue,
+ false);
+ }
+
+ private Money calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
+ final MathContext mc, final int daysInPeriod, final Money outstandingBalance) {
+
+ Money interestDue = Money.zero(outstandingBalance.getCurrency());
+
+ switch (this.interestCalculationPeriodMethod) {
+ case DAILY:
+ BigDecimal dailyInterestRate = this.annualNominalInterestRate.divide(BigDecimal.valueOf(Long.valueOf(365)), mc)
+ .divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc).multiply(BigDecimal.valueOf(this.repaymentEvery));
+
+ BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
+
+ interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, mc.getRoundingMode());
+ break;
+ case SAME_AS_REPAYMENT_PERIOD:
+ final BigDecimal periodicInterestRate = periodicInterestRate(calculator, mc);
+ interestDue = outstandingBalance.multiplyRetainScale(periodicInterestRate, mc.getRoundingMode());
+ break;
+ case INVALID:
+ break;
+ }
+
+ return interestDue;
+ }
+
+ private Money calculateDecliningInterestDueForInstallmentAfterApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
+ final double interestCalculationGraceOnRepaymentPeriodFraction, final MathContext mc, final int daysInPeriod,
+ final Money outstandingBalance, final int periodNumber) {
+
+ Money interest = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc, daysInPeriod, outstandingBalance);
+
+ if (isInterestPaymentGraceApplicableForThisPeriod(periodNumber)) {
+ interest = interest.zero();
+ }
+
+ Double fraction = interestCalculationGraceOnRepaymentPeriodFraction;
+
+ if (isInterestFreeGracePeriod(periodNumber)) {
+ interest = interest.zero();
+ } else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
+
+ if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
+ interest = interest.zero();
+ fraction = fraction - Integer.valueOf(1).doubleValue();
+
+ } else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25")
+ && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
+
+ Money graceOnInterestForRepaymentPeriod = interest.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
+ interest = interest.minus(graceOnInterestForRepaymentPeriod);
+ fraction = Double.valueOf("0");
+ }
+ }
+
+ return interest;
+ }
+
+ private boolean isInterestFreeGracePeriodFromDate(final double interestCalculationGraceOnRepaymentPeriodFraction) {
+ return this.interestChargedFromDate != null && interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.0");
+ }
+
+ private Money calculateEqualPrincipalDueForInstallment(final MathContext mc, final int periodNumber) {
+
+ final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfPrincipalPaymentPeriods(this.numberOfRepayments);
+
+ Money principal = this.principal.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
+ if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
+ principal = principal.zero();
+ }
+ return principal;
+ }
+
+ private Money calculatePrincipalDueForInstallment(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc,
+ final int periodNumber, final Money totalDuePerInstallment, final int daysInPeriod, final Money outstandingBalance) {
+
+ Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc,
+ daysInPeriod, outstandingBalance);
+
+ Money principal = totalDuePerInstallment.minus(interestForThisInstallmentBeforeGrace);
+ if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
+ principal = principal.zero();
+ }
+ return principal;
+ }
+
+ private Money calculateTotalDueForEqualInstallmentRepaymentPeriod(final BigDecimal periodicInterestRate, final Money balance,
+ final int periodsElapsed) {
+
+ final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, balance, periodsElapsed);
+
+ return Money.of(balance.getCurrency(), BigDecimal.valueOf(paymentPerRepaymentPeriod));
+ }
+
+ public LoanProductRelatedDetail toLoanProductRelatedDetail() {
+ final MonetaryCurrency currency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces());
+
+ return LoanProductRelatedDetail.createFrom(currency, this.principal.getAmount(), this.interestRatePerPeriod,
+ this.interestRatePeriodFrequencyType, this.annualNominalInterestRate, this.interestMethod,
+ this.interestCalculationPeriodMethod, this.repaymentEvery, this.repaymentPeriodFrequencyType, this.numberOfRepayments,
+ this.principalGrace, this.interestPaymentGrace, this.interestChargingGrace, this.amortizationMethod,
+ this.inArrearsTolerance.getAmount());
+ }
+
+ public Integer getLoanTermFrequency() {
+ return this.loanTermFrequency;
+ }
+
+ public PeriodFrequencyType getLoanTermPeriodFrequencyType() {
+ return this.loanTermPeriodFrequencyType;
+ }
+
+ public Integer getRepaymentEvery() {
+ return this.repaymentEvery;
+ }
+
+ public PeriodFrequencyType getRepaymentPeriodFrequencyType() {
+ return this.repaymentPeriodFrequencyType;
+ }
+
+ public Date getRepaymentStartFromDate() {
+ Date dateValue = null;
+ if (this.repaymentsStartingFromDate != null) {
+ dateValue = this.repaymentsStartingFromDate.toDate();
+ }
+ return dateValue;
+ }
+
+ public Date getInterestChargedFromDate() {
+ Date dateValue = null;
+ if (this.interestChargedFromDate != null) {
+ dateValue = this.interestChargedFromDate.toDate();
+ }
+ return dateValue;
+ }
+
+ public LocalDate getInterestChargedFromLocalDate() {
+ return this.interestChargedFromDate;
+ }
+
+ public InterestMethod getInterestMethod() {
+ return this.interestMethod;
+ }
+
+ public AmortizationMethod getAmortizationMethod() {
+ return this.amortizationMethod;
+ }
+
+ public MonetaryCurrency getCurrency() {
+ return this.principal.getCurrency();
+ }
+
+ public Integer getNumberOfRepayments() {
+ return this.numberOfRepayments;
+ }
+
+ public LocalDate getExpectedDisbursementDate() {
+ return this.expectedDisbursementDate;
+ }
+
+ public LocalDate getRepaymentsStartingFromLocalDate() {
+ return this.repaymentsStartingFromDate;
+ }
+
+ public Money getPrincipal() {
+ return this.principal;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java
deleted file mode 100644
index efa357a64..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanSchedule.java
+++ /dev/null
@@ -1,109 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-import java.util.Set;
-
-import org.joda.time.LocalDate;
-import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
-
-/**
- * Domain representation of a Loan Schedule (not used for persistence)
- */
-public final class LoanSchedule {
-
- private final LoanScheduleGenerator loanScheduleGenerator;
- private final ApplicationCurrency applicationCurrency;
- private final BigDecimal principal;
- private final BigDecimal nominalInterestRatePerPeriod;
- private final PeriodFrequencyType interestRatePeriodFrequencyType;
- private final BigDecimal nominalAnnualInterestRate;
- private final InterestMethod interestMethod;
- private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
- private final Integer repaymentEvery;
- private final PeriodFrequencyType repaymentPeriodFrequencyType;
- private final Integer numberOfRepayments;
- private final AmortizationMethod amortizationMethod;
- private final Integer loanTermFrequency;
- private final PeriodFrequencyType loanTermPeriodFrequencyType;
- private final LocalDate disbursementDate;
- private final LocalDate repaymentStartFromDate;
- private final LocalDate interestChargedFromDate;
- private final BigDecimal inArrearsTolerance;
-
- private final Set loanCharges;
-
- public LoanSchedule(final LoanScheduleGenerator loanScheduleGenerator, final ApplicationCurrency applicationCurrency,
- final BigDecimal principal, final BigDecimal nominalInterestRatePerPeriod,
- final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalAnnualInterestRate,
- final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
- final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
- final AmortizationMethod amortizationMethod, final Integer loanTermFrequency,
- final PeriodFrequencyType loanTermPeriodFrequencyType, final Set loanCharges, final LocalDate disbursementDate,
- final LocalDate repaymentStartFromDate, final LocalDate interestChargedFromDate, final BigDecimal inArrearsTolerance) {
- this.loanScheduleGenerator = loanScheduleGenerator;
- this.applicationCurrency = applicationCurrency;
- this.principal = principal;
- this.nominalInterestRatePerPeriod = nominalInterestRatePerPeriod;
- this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
- this.nominalAnnualInterestRate = nominalAnnualInterestRate;
- this.interestMethod = interestMethod;
- this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
- this.repaymentEvery = repaymentEvery;
- this.repaymentPeriodFrequencyType = repaymentPeriodFrequencyType;
- this.numberOfRepayments = numberOfRepayments;
- this.amortizationMethod = amortizationMethod;
- this.loanTermFrequency = loanTermFrequency;
- this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
- this.loanCharges = loanCharges;
- this.disbursementDate = disbursementDate;
- this.repaymentStartFromDate = repaymentStartFromDate;
- this.interestChargedFromDate = interestChargedFromDate;
- this.inArrearsTolerance = inArrearsTolerance;
- }
-
- public LoanScheduleData generate() {
- return loanScheduleGenerator.generate(applicationCurrency, loanProductRelatedDetail(), loanTermFrequency,
- loanTermPeriodFrequencyType, disbursementDate, repaymentStartFromDate, interestChargedFromDate, loanCharges);
- }
-
- public LoanProductRelatedDetail loanProductRelatedDetail() {
- final MonetaryCurrency currency = new MonetaryCurrency(applicationCurrency.getCode(), applicationCurrency.getDecimalPlaces());
-
- return LoanProductRelatedDetail.createFrom(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
- nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType,
- numberOfRepayments, amortizationMethod, this.inArrearsTolerance);
- }
-
- public Integer getLoanTermFrequency() {
- return this.loanTermFrequency;
- }
-
- public PeriodFrequencyType getLoanTermPeriodFrequencyType() {
- return this.loanTermPeriodFrequencyType;
- }
-
- public LocalDate getDisbursementDate() {
- return this.disbursementDate;
- }
-
- public LocalDate getRepaymentStartFromDate() {
- return this.repaymentStartFromDate;
- }
-
- public LocalDate getInterestChargedFromDate() {
- return this.interestChargedFromDate;
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
index 376296be7..721fad5fb 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
@@ -5,19 +5,14 @@
*/
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
+import java.math.MathContext;
import java.util.Set;
-import org.joda.time.LocalDate;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public interface LoanScheduleGenerator {
- LoanScheduleData generate(ApplicationCurrency applicationCurrency, LoanProductRelatedDetail loanScheduleInfo,
- Integer loanTermFrequency, PeriodFrequencyType loanTermFrequencyType, LocalDate disbursementDate, LocalDate firstRepaymentDate,
- LocalDate interestCalculatedFrom, Set loanCharges);
-
+ LoanScheduleModel generate(MathContext mc, ApplicationCurrency applicationCurrency, LoanApplicationTerms loanApplicationTerms,
+ Set loanCharges);
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorFactory.java
index f8e542094..d3e946981 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorFactory.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorFactory.java
@@ -10,5 +10,4 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
public interface LoanScheduleGeneratorFactory {
LoanScheduleGenerator create(InterestMethod interestMethod);
-
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
new file mode 100644
index 000000000..0a8ea9e54
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModel.java
@@ -0,0 +1,85 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.mifosplatform.organisation.monetary.data.CurrencyData;
+import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
+
+/**
+ * Domain representation of a Loan Schedule (not used for persistence)
+ */
+public final class LoanScheduleModel {
+
+ private final Collection periods;
+ private final ApplicationCurrency applicationCurrency;
+ private final int loanTermInDays;
+ private final Money totalPrincipalDisbursed;
+ private final BigDecimal totalPrincipalExpected;
+ private final BigDecimal totalPrincipalPaid;
+ private final BigDecimal totalInterestCharged;
+ private final BigDecimal totalFeeChargesCharged;
+ private final BigDecimal totalPenaltyChargesCharged;
+ private final BigDecimal totalRepaymentExpected;
+ private final BigDecimal totalOutstanding;
+
+ public static LoanScheduleModel from(final Collection periods, final ApplicationCurrency applicationCurrency,
+ final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
+ final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
+ final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
+
+ return new LoanScheduleModel(periods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
+ totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
+ totalOutstanding);
+ }
+
+ private LoanScheduleModel(final Collection periods, final ApplicationCurrency applicationCurrency,
+ final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
+ final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
+ final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
+ this.periods = periods;
+ this.applicationCurrency = applicationCurrency;
+ this.loanTermInDays = loanTermInDays;
+ this.totalPrincipalDisbursed = principalDisbursed;
+ this.totalPrincipalExpected = totalPrincipalExpected;
+ this.totalPrincipalPaid = totalPrincipalPaid;
+ this.totalInterestCharged = totalInterestCharged;
+ this.totalFeeChargesCharged = totalFeeChargesCharged;
+ this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
+ this.totalRepaymentExpected = totalRepaymentExpected;
+ this.totalOutstanding = totalOutstanding;
+ }
+
+ public LoanScheduleData toData() {
+
+ final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal();
+ final CurrencyData currency = applicationCurrency.toData(decimalPlaces);
+
+ final Collection periodsData = new ArrayList();
+ for (LoanScheduleModelPeriod modelPeriod : this.periods) {
+ periodsData.add(modelPeriod.toData());
+ }
+
+ BigDecimal totalWaived = null;
+ BigDecimal totalWrittenOff = null;
+ BigDecimal totalRepayment = null;
+
+ return new LoanScheduleData(currency, periodsData, this.loanTermInDays, this.totalPrincipalDisbursed.getAmount(),
+ this.totalPrincipalExpected, this.totalPrincipalPaid, this.totalInterestCharged, this.totalFeeChargesCharged,
+ this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, this.totalRepaymentExpected, totalRepayment,
+ this.totalOutstanding);
+ }
+
+ public Collection getPeriods() {
+ return this.periods;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java
new file mode 100644
index 000000000..61429b3ac
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelDisbursementPeriod.java
@@ -0,0 +1,86 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
+
+/**
+ * Domain representation of a Loan Schedule Disbursement Period (not used for
+ * persistence)
+ */
+public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleModelPeriod {
+
+ @SuppressWarnings("unused")
+ private final int periodNumber;
+ private final LocalDate disbursementDate;
+ private final Money principalDisbursed;
+ private final BigDecimal chargesDueAtTimeOfDisbursement;
+
+ public static LoanScheduleModelDisbursementPeriod disbursement(final LoanApplicationTerms loanApplicationTerms,
+ final BigDecimal chargesDueAtTimeOfDisbursement) {
+
+ final int periodNumber = 0;
+ return new LoanScheduleModelDisbursementPeriod(periodNumber, loanApplicationTerms, chargesDueAtTimeOfDisbursement);
+ }
+
+ private LoanScheduleModelDisbursementPeriod(final int periodNumber, final LoanApplicationTerms loanApplicationTerms,
+ final BigDecimal chargesDueAtTimeOfDisbursement) {
+ this.periodNumber = periodNumber;
+ this.disbursementDate = loanApplicationTerms.getExpectedDisbursementDate();
+ this.principalDisbursed = loanApplicationTerms.getPrincipal();
+ this.chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement;
+ }
+
+ @Override
+ public LoanSchedulePeriodData toData() {
+ return LoanSchedulePeriodData.disbursementOnlyPeriod(this.disbursementDate, this.principalDisbursed.getAmount(),
+ this.chargesDueAtTimeOfDisbursement, false);
+ }
+
+ @Override
+ public boolean isRepaymentPeriod() {
+ return false;
+ }
+
+ @Override
+ public Integer periodNumber() {
+ return null;
+ }
+
+ @Override
+ public LocalDate periodFromDate() {
+ return null;
+ }
+
+ @Override
+ public LocalDate periodDueDate() {
+ return null;
+ }
+
+ @Override
+ public BigDecimal principalDue() {
+ return null;
+ }
+
+ @Override
+ public BigDecimal interestDue() {
+ return null;
+ }
+
+ @Override
+ public BigDecimal feeChargesDue() {
+ return null;
+ }
+
+ @Override
+ public BigDecimal penaltyChargesDue() {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java
new file mode 100644
index 000000000..5d7780033
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelPeriod.java
@@ -0,0 +1,32 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
+
+public interface LoanScheduleModelPeriod {
+
+ LoanSchedulePeriodData toData();
+
+ boolean isRepaymentPeriod();
+
+ Integer periodNumber();
+
+ LocalDate periodFromDate();
+
+ LocalDate periodDueDate();
+
+ BigDecimal principalDue();
+
+ BigDecimal interestDue();
+
+ BigDecimal feeChargesDue();
+
+ BigDecimal penaltyChargesDue();
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java
new file mode 100644
index 000000000..87727cb51
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleModelRepaymentPeriod.java
@@ -0,0 +1,118 @@
+/**
+ * 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.loanschedule.domain;
+
+import java.math.BigDecimal;
+
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
+
+/**
+ * Domain representation of a Loan Schedule Repayment Period (not used for
+ * persistence)
+ */
+public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModelPeriod {
+
+ private final int periodNumber;
+ private final LocalDate fromDate;
+ private final LocalDate dueDate;
+ private final Money principalDue;
+ private final Money outstandingLoanBalance;
+ private final Money interestDue;
+ private final Money feeChargesDue;
+ private final Money penaltyChargesDue;
+ private final Money totalDue;
+
+ public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
+ final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
+ final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue) {
+
+ return new LoanScheduleModelRepaymentPeriod(periodNumber, startDate, scheduledDueDate, principalDue, outstandingLoanBalance,
+ interestDue, feeChargesDue, penaltyChargesDue, totalDue);
+ }
+
+ public LoanScheduleModelRepaymentPeriod(final int periodNumber, final LocalDate fromDate, final LocalDate dueDate,
+ final Money principalDue, final Money outstandingLoanBalance, final Money interestDue, final Money feeChargesDue,
+ final Money penaltyChargesDue, final Money totalDue) {
+ this.periodNumber = periodNumber;
+ this.fromDate = fromDate;
+ this.dueDate = dueDate;
+ this.principalDue = principalDue;
+ this.outstandingLoanBalance = outstandingLoanBalance;
+ this.interestDue = interestDue;
+ this.feeChargesDue = feeChargesDue;
+ this.penaltyChargesDue = penaltyChargesDue;
+ this.totalDue = totalDue;
+ }
+
+ @Override
+ public LoanSchedulePeriodData toData() {
+ return LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, this.fromDate, this.dueDate, this.principalDue.getAmount(),
+ this.outstandingLoanBalance.getAmount(), this.interestDue.getAmount(), this.feeChargesDue.getAmount(),
+ this.penaltyChargesDue.getAmount(), this.totalDue.getAmount());
+ }
+
+ @Override
+ public boolean isRepaymentPeriod() {
+ return true;
+ }
+
+ @Override
+ public Integer periodNumber() {
+ return this.periodNumber;
+ }
+
+ @Override
+ public LocalDate periodFromDate() {
+ return this.fromDate;
+ }
+
+ @Override
+ public LocalDate periodDueDate() {
+ return this.dueDate;
+ }
+
+ @Override
+ public BigDecimal principalDue() {
+ BigDecimal value = null;
+ if (this.principalDue != null) {
+ value = this.principalDue.getAmount();
+ }
+
+ return value;
+ }
+
+ @Override
+ public BigDecimal interestDue() {
+ BigDecimal value = null;
+ if (this.interestDue != null) {
+ value = this.interestDue.getAmount();
+ }
+
+ return value;
+ }
+
+ @Override
+ public BigDecimal feeChargesDue() {
+ BigDecimal value = null;
+ if (this.feeChargesDue != null) {
+ value = this.feeChargesDue.getAmount();
+ }
+
+ return value;
+ }
+
+ @Override
+ public BigDecimal penaltyChargesDue() {
+ BigDecimal value = null;
+ if (this.penaltyChargesDue != null) {
+ value = this.penaltyChargesDue.getAmount();
+ }
+
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PaymentPeriodsInOneYearCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PaymentPeriodsInOneYearCalculator.java
index a170cd71a..c8b5ae3d0 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PaymentPeriodsInOneYearCalculator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PaymentPeriodsInOneYearCalculator.java
@@ -5,8 +5,6 @@
*/
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
-import java.util.List;
-
import org.joda.time.LocalDate;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
@@ -14,9 +12,7 @@ public interface PaymentPeriodsInOneYearCalculator {
Integer calculate(PeriodFrequencyType repaymentFrequencyType);
- double calculateRepaymentPeriodAsAFractionOfDays(
- PeriodFrequencyType repaymentPeriodFrequencyType,
- Integer every, LocalDate interestCalculatedFrom,
- List scheduledDates, LocalDate disbursementDate);
+ double calculatePortionOfRepaymentPeriodInterestChargingGrace(LocalDate repaymentPeriodStartDate, LocalDate scheduledDueDate,
+ LocalDate interestChargedFromLocalDate, PeriodFrequencyType repaymentPeriodFrequencyType, Integer repaidEvery);
-}
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PeriodicInterestRateCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PeriodicInterestRateCalculator.java
index 846ad07a4..5c2aa2def 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PeriodicInterestRateCalculator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PeriodicInterestRateCalculator.java
@@ -13,75 +13,74 @@ import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
+@Deprecated
public class PeriodicInterestRateCalculator {
- private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
-
- public BigDecimal calculateFrom(final PeriodFrequencyType loanTermFrequencyType, final BigDecimal annualNominalInterestRate) {
-
- MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
-
- Integer paymentPeriodsInOneYear = this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType);
-
- BigDecimal divisor = BigDecimal.valueOf(paymentPeriodsInOneYear * 100);
- return annualNominalInterestRate.divide(divisor, mc);
- }
-
- public BigDecimal calculateFrom(final LoanProductRelatedDetail loanScheduleInfo) {
-
- MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
-
- Integer paymentPeriodsInOneYear = this.paymentPeriodsInOneYearCalculator.calculate(loanScheduleInfo.getRepaymentPeriodFrequencyType());
-
- BigDecimal divisor = BigDecimal.valueOf(paymentPeriodsInOneYear * 100);
- BigDecimal numberOfPeriods = BigDecimal.valueOf(loanScheduleInfo.getRepayEvery());
-
- return loanScheduleInfo.getAnnualNominalInterestRate().divide(divisor, mc).multiply(numberOfPeriods);
- }
+ private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
- public Money calculateInterestOn(
- final Money outstandingBalance,
- final BigDecimal periodInterestRateForRepaymentPeriod,
- final int daysInPeriod,
- final LoanProductRelatedDetail loanScheduleInfo) {
+ public BigDecimal calculateFrom(final PeriodFrequencyType loanTermFrequencyType, final BigDecimal annualNominalInterestRate) {
- MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
- Money interestDue = Money.zero(outstandingBalance.getCurrency());
-
- switch (loanScheduleInfo.getInterestCalculationPeriodMethod()) {
- case DAILY:
- BigDecimal dailyInterestRate = loanScheduleInfo.getAnnualNominalInterestRate()
- .divide(BigDecimal.valueOf(Long.valueOf(365)), mc)
- .divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
- .multiply(BigDecimal.valueOf(loanScheduleInfo.getRepayEvery()));
-
- BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
-
- interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, RoundingMode.HALF_EVEN);
- break;
- default:
- interestDue = outstandingBalance.multiplyRetainScale(periodInterestRateForRepaymentPeriod, RoundingMode.HALF_EVEN);
- break;
- }
-
- return interestDue;
- }
+ MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
- public Money calculatePrincipalOn(Money totalDuePerInstallment, Money interestForInstallment, LoanProductRelatedDetail loanScheduleInfo) {
-
- Money principalDue = Money.zero(totalDuePerInstallment.getCurrency());
-
- switch (loanScheduleInfo.getAmortizationMethod()) {
- case EQUAL_PRINCIPAL:
- principalDue = loanScheduleInfo.getPrincipal().dividedBy(loanScheduleInfo.getNumberOfRepayments(), RoundingMode.HALF_EVEN);
- break;
- case EQUAL_INSTALLMENTS:
- principalDue = totalDuePerInstallment.minus(interestForInstallment);
- break;
- case INVALID:
- break;
- }
-
- return principalDue;
- }
+ Integer paymentPeriodsInOneYear = this.paymentPeriodsInOneYearCalculator.calculate(loanTermFrequencyType);
+
+ BigDecimal divisor = BigDecimal.valueOf(paymentPeriodsInOneYear * 100);
+ return annualNominalInterestRate.divide(divisor, mc);
+ }
+
+ public BigDecimal calculateFrom(final LoanProductRelatedDetail loanScheduleInfo) {
+
+ MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
+
+ Integer paymentPeriodsInOneYear = this.paymentPeriodsInOneYearCalculator.calculate(loanScheduleInfo
+ .getRepaymentPeriodFrequencyType());
+
+ BigDecimal divisor = BigDecimal.valueOf(paymentPeriodsInOneYear * 100);
+ BigDecimal numberOfPeriods = BigDecimal.valueOf(loanScheduleInfo.getRepayEvery());
+
+ return loanScheduleInfo.getAnnualNominalInterestRate().divide(divisor, mc).multiply(numberOfPeriods);
+ }
+
+ public Money calculateInterestOn(final Money outstandingBalance, final BigDecimal periodInterestRateForRepaymentPeriod,
+ final int daysInPeriod, final LoanProductRelatedDetail loanScheduleInfo) {
+
+ MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN);
+ Money interestDue = Money.zero(outstandingBalance.getCurrency());
+
+ switch (loanScheduleInfo.getInterestCalculationPeriodMethod()) {
+ case DAILY:
+ BigDecimal dailyInterestRate = loanScheduleInfo.getAnnualNominalInterestRate()
+ .divide(BigDecimal.valueOf(Long.valueOf(365)), mc).divide(BigDecimal.valueOf(Double.valueOf("100.0")), mc)
+ .multiply(BigDecimal.valueOf(loanScheduleInfo.getRepayEvery()));
+
+ BigDecimal equivalentInterestRateForPeriod = dailyInterestRate.multiply(BigDecimal.valueOf(Long.valueOf(daysInPeriod)));
+
+ interestDue = outstandingBalance.multiplyRetainScale(equivalentInterestRateForPeriod, RoundingMode.HALF_EVEN);
+ break;
+ default:
+ interestDue = outstandingBalance.multiplyRetainScale(periodInterestRateForRepaymentPeriod, RoundingMode.HALF_EVEN);
+ break;
+ }
+
+ return interestDue;
+ }
+
+ public Money calculatePrincipalOn(final Money totalDuePerInstallment, final Money interestForInstallment,
+ final LoanProductRelatedDetail loanScheduleInfo) {
+
+ Money principalDue = Money.zero(totalDuePerInstallment.getCurrency());
+
+ switch (loanScheduleInfo.getAmortizationMethod()) {
+ case EQUAL_PRINCIPAL:
+ principalDue = loanScheduleInfo.getPrincipal().dividedBy(loanScheduleInfo.getNumberOfRepayments(), RoundingMode.HALF_EVEN);
+ break;
+ case EQUAL_INSTALLMENTS:
+ principalDue = totalDuePerInstallment.minus(interestForInstallment);
+ break;
+ case INVALID:
+ break;
+ }
+
+ return principalDue;
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PmtCalculator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PmtCalculator.java
deleted file mode 100644
index 929a36ba0..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PmtCalculator.java
+++ /dev/null
@@ -1,100 +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.loanschedule.domain;
-
-import java.math.BigDecimal;
-
-import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
-import org.mifosplatform.organisation.monetary.domain.Money;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
-
-public class PmtCalculator {
-
- public Money calculatePaymentForOnePeriodFrom(final LoanProductRelatedDetail loanScheduleInfo,
- final BigDecimal periodInterestRateForRepaymentPeriod,
- final MonetaryCurrency monetaryCurrency) {
- double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
-
- double futureValue = 0;
- double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
- double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
-
- // work out the total payment (principal + interest components) for each installment due.
- // use the period type and interest as expressed e.g. 2% per month or 24% per year even is 'daily' interest calculation is selected
- double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
-
- return Money.of(monetaryCurrency, BigDecimal.valueOf(paymentPerInstallment));
- }
-
- public Money calculateTotalRepaymentFrom(final LoanProductRelatedDetail loanScheduleInfo,
- final BigDecimal periodInterestRateForRepaymentPeriod,
- final MonetaryCurrency monetaryCurrency) {
- double interestRateFraction = periodInterestRateForRepaymentPeriod.doubleValue();
-
- double futureValue = 0;
- double numberOfPeriods = loanScheduleInfo.getNumberOfRepayments().doubleValue();
- double principal = loanScheduleInfo.getPrincipal().getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
-
- // work out the total payment (principal + interest components) for each installment due.
- // use the period type and interest as expressed e.g. 2% per month or 24% per year even is 'daily' interest calculation is selected
- double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principal, futureValue, false);
-
- double totalRepayment = paymentPerInstallment * loanScheduleInfo.getNumberOfRepayments();
-
- return Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
- }
-
- public Money calculateTotalRepaymentFrom(
- final Money principal,
- final Integer loanTermFrequency,
- final BigDecimal periodInterestRateForLoanTermPeriod,
- final MonetaryCurrency monetaryCurrency) {
-
- double interestRateFraction = periodInterestRateForLoanTermPeriod.doubleValue();
-
- double futureValue = 0;
- double numberOfPeriods = loanTermFrequency.doubleValue();
- double principalAmount = principal.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
-
- // work out the total payment (principal + interest components) for each installment due.
- // use the period type and interest as expressed e.g. 2% per month or 24% per year even is 'daily' interest calculation is selected
- double paymentPerInstallment = pmt(interestRateFraction, numberOfPeriods, principalAmount, futureValue, false);
-
- double totalRepayment = paymentPerInstallment * loanTermFrequency;
-
- return Money.of(monetaryCurrency, BigDecimal.valueOf(totalRepayment));
- }
-
- /**
- * PMT calculates a fixed monthly payment to be paid by borrower every 'period' to ensure loan is paid off in full (with interest).
- *
- * This monthly payment c depends upon
- * the monthly interest rate r (expressed as a fraction, not a percentage,
- * i.e., divide the quoted yearly percentage rate by 100 and by 12 to obtain
- * the monthly interest rate), the number of monthly payments N called the
- * loan's term, and the amount borrowed P known as the loan's principal; c
- * is given by the formula:
- *
- * c = (r / (1 - (1 + r)^-N))P
- *
- * @param interestRateFraction
- * @param numberOfPayments
- * @param principal
- * @param futureValue
- * @param type
- */
- private static double pmt(double interestRateFraction, double numberOfPayments, double principal, double futureValue, boolean type) {
- double payment = 0;
- if (interestRateFraction == 0) {
- payment = -1 * (futureValue + principal) / numberOfPayments;
- } else {
- double r1 = interestRateFraction + 1;
- payment = (futureValue + principal * Math.pow(r1, numberOfPayments)) * interestRateFraction
- / ((type ? r1 : 1) * (1 - Math.pow(r1, numberOfPayments)));
- }
- return payment;
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PrincipalInterest.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PrincipalInterest.java
new file mode 100644
index 000000000..812263add
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/PrincipalInterest.java
@@ -0,0 +1,33 @@
+/**
+ * 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.loanschedule.domain;
+
+import org.mifosplatform.organisation.monetary.domain.Money;
+
+public class PrincipalInterest {
+
+ private final Money principal;
+ private final Money interest;
+ private final Money interestPaymentDueToGrace;
+
+ public PrincipalInterest(final Money principal, final Money interest, final Money interestPaymentDueToGrace) {
+ this.principal = principal;
+ this.interest = interest;
+ this.interestPaymentDueToGrace = interestPaymentDueToGrace;
+ }
+
+ public Money principal() {
+ return this.principal;
+ }
+
+ public Money interest() {
+ return this.interest;
+ }
+
+ public Money interestPaymentDueToGrace() {
+ return this.interestPaymentDueToGrace;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
index 2b374933d..ba8722e26 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/ScheduledDateGenerator.java
@@ -8,17 +8,12 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
import java.util.List;
import org.joda.time.LocalDate;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
-import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
+import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public interface ScheduledDateGenerator {
- List generate(
- LoanProductMinimumRepaymentScheduleRelatedDetail loanScheduleDateInfo,
- LocalDate disbursementDate, LocalDate firstRepaymentDate);
-
- LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(
- LoanProductRelatedDetail loanScheduleInfo,
- List scheduledDates);
+ List generate(LoanApplicationTerms loanApplicationTerms);
+ LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(PeriodFrequencyType repaymentPeriodFrequencyType, int repaidEvery,
+ List scheduledDates);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index 8a98e7a63..980fe3554 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -6,6 +6,8 @@
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
import java.util.Set;
import org.joda.time.LocalDate;
@@ -13,16 +15,19 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeAssembler;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
@@ -54,65 +59,96 @@ public class LoanScheduleAssembler {
this.loanChargeAssembler = loanChargeAssembler;
}
- public LoanSchedule fromJson(final String json) {
- final JsonElement element = fromApiJsonHelper.parse(json);
- return fromJson(element);
- }
-
- public LoanSchedule fromJson(final JsonElement element) {
- return fromJson(element, null);
- }
-
- public LoanSchedule fromJson(final JsonElement element, final BigDecimal inArrearsTolerance) {
+ public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
final Long loanProductId = fromApiJsonHelper.extractLongNamed("productId", element);
final LoanProduct loanProduct = this.loanProductRepository.findOne(loanProductId);
if (loanProduct == null) { throw new LoanProductNotFoundException(loanProductId); }
+ return assembleLoanApplicationTermsFrom(element, loanProduct);
+ }
+
+ private LoanApplicationTerms assembleLoanApplicationTermsFrom(final JsonElement element, final LoanProduct loanProduct) {
+
final MonetaryCurrency currency = loanProduct.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
- final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
- final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
- //final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
- final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
- final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
- element);
-
- final InterestMethod interestMethod = InterestMethod.fromInt(interestType);
- //final PeriodFrequencyType interestRatePeriodFrequencyType = PeriodFrequencyType.fromInt(interestRateFrequencyType);
-
- // PeriodFrequencyType is copied from Loan Product
- final PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.loanProductRelatedDetail().getInterestPeriodFrequencyType();
- final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
- .fromInt(interestCalculationPeriodType);
-
- final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType,
- interestRatePerPeriod);
-
- final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
- final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
- final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
- final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
- final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyType);
- final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
-
+ // loan terms
final Integer loanTermFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequency", element);
final Integer loanTermFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("loanTermFrequencyType", element);
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(loanTermFrequencyType);
- final LocalDate expectedDisbursementDate = fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
- final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
+ final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
+ final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
+ final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
+ final PeriodFrequencyType repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(repaymentFrequencyType);
+
+ final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
+ final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(amortizationType);
+
+ // interest terms
+ final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
+ final InterestMethod interestMethod = InterestMethod.fromInt(interestType);
+
+ final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
+ element);
+ final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
+ .fromInt(interestCalculationPeriodType);
+
+ final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
+ final PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.getInterestPeriodFrequencyType();
+
+ final BigDecimal annualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType,
+ interestRatePerPeriod);
+
+ // disbursement details
+ final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
+ final Money principalMoney = Money.of(currency, principal);
+
+ final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
+ final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
+
+ // grace details
+ final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
+ final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
+ final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
final LocalDate interestChargedFromDate = fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element);
+ // other
+ final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
+ final Money inArrearsToleranceMoney = Money.of(currency, inArrearsTolerance);
+
+ return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
+ repaymentEvery, repaymentPeriodFrequencyType, amortizationMethod, interestMethod, interestRatePerPeriod,
+ interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney,
+ expectedDisbursementDate, repaymentsStartingFromDate, graceOnPrincipalPayment, graceOnInterestPayment,
+ graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney);
+ }
+
+ public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element) {
+ LoanApplicationTerms loanApplicationTerms = assembleLoanTerms(element);
+ return loanApplicationTerms.toLoanProductRelatedDetail();
+ }
+
+ public LoanScheduleModel assembleLoanScheduleFrom(final JsonElement element) {
+
+ final LoanApplicationTerms loanApplicationTerms = assembleLoanTerms(element);
+
+ return assembleLoanScheduleFrom(loanApplicationTerms, element);
+ }
+
+ public LoanScheduleModel assembleLoanScheduleFrom(final LoanApplicationTerms loanApplicationTerms, final JsonElement element) {
+
final Set loanCharges = this.loanChargeAssembler.fromParsedJson(element);
- final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
+ final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
- return new LoanSchedule(loanScheduleGenerator, applicationCurrency, principal, interestRatePerPeriod,
- interestRatePeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod,
- repaymentEvery, repaymentPeriodFrequencyType, numberOfRepayments, amortizationMethod, loanTermFrequency,
- loanTermPeriodFrequencyType, loanCharges, expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
- inArrearsTolerance);
+ final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
+ final MathContext mc = new MathContext(8, roundingMode);
+
+ final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
+ final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
+
+ return loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, loanCharges);
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
index a8cc2b393..e95a84fe5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformService.java
@@ -6,9 +6,9 @@
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
import org.mifosplatform.infrastructure.core.api.JsonQuery;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
public interface LoanScheduleCalculationPlatformService {
- LoanScheduleData calculateLoanSchedule(JsonQuery query);
+ LoanScheduleModel calculateLoanSchedule(JsonQuery query);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
index b43bc59b2..11619237b 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -6,9 +6,7 @@
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
import org.mifosplatform.infrastructure.core.api.JsonQuery;
-import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanSchedule;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.serialization.CalculateLoanScheduleQueryFromApiJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -16,26 +14,21 @@ import org.springframework.stereotype.Service;
@Service
public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleCalculationPlatformService {
- private final PlatformSecurityContext context;
private final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer;
private final LoanScheduleAssembler loanScheduleAssembler;
@Autowired
- public LoanScheduleCalculationPlatformServiceImpl(final PlatformSecurityContext context,
- final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer,
+ public LoanScheduleCalculationPlatformServiceImpl(final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer,
final LoanScheduleAssembler loanScheduleAssembler) {
- this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanScheduleAssembler = loanScheduleAssembler;
}
@Override
- public LoanScheduleData calculateLoanSchedule(final JsonQuery query) {
- context.authenticatedUser();
+ public LoanScheduleModel calculateLoanSchedule(final JsonQuery query) {
this.fromApiJsonDeserializer.validate(query.json());
- final LoanSchedule loanSchedule = this.loanScheduleAssembler.fromJson(query.parsedJson());
- return loanSchedule.generate();
+ return this.loanScheduleAssembler.assembleLoanScheduleFrom(query.parsedJson());
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
index 8d4215b1a..585cf2186 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java
@@ -40,8 +40,9 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
"productId", "accountNo", "externalId", "fundId", "loanOfficerId", "loanPurposeId", "transactionProcessingStrategyId",
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "loanTermFrequency",
"loanTermFrequencyType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType",
- "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate",
- "submittedOnDate", "submittedOnNote", "locale", "dateFormat", "charges", "collateral"));
+ "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment",
+ "graceOnInterestPayment", "graceOnInterestCharged", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale",
+ "dateFormat", "charges", "collateral"));
private final FromJsonHelper fromApiJsonHelper;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
index f31d7b1dd..b6be5dddc 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
@@ -40,8 +40,9 @@ public final class LoanApplicationCommandFromApiJsonHelper {
*/
final Set supportedParameters = new HashSet(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId",
"loanType", "productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery",
- "repaymentFrequencyType", "interestRatePerPeriod", "amortizationType", "interestType",
- "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate",
+ "repaymentFrequencyType", "interestRatePerPeriod", "amortizationType", "interestType", "interestCalculationPeriodType",
+ "expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment", "graceOnInterestPayment",
+ "graceOnInterestCharged", "interestChargedFromDate", //
"submittedOnDate", "submittedOnNote", //
"accountNo", "externalId", "fundId", "loanOfficerId", // optional
"loanPurposeId", "inArrearsTolerance", "charges", "collateral", // optional
@@ -50,7 +51,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
));
private final FromJsonHelper fromApiJsonHelper;
-
+
@Autowired
public LoanApplicationCommandFromApiJsonHelper(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
@@ -70,14 +71,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final String loanTypeParameterName = "loanType";
final String loanTypeStr = fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull();
-
- if(!StringUtils.isBlank(loanTypeStr)){
+
+ if (!StringUtils.isBlank(loanTypeStr)) {
final LoanType loanType = LoanType.fromName(loanTypeStr);
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanType.getValue()).inMinMaxRange(1, 3);
-
+
final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element);
final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element);
- if(loanType.isIndividualLoan()){
+ if (loanType.isIndividualLoan()) {
baseDataValidator.reset().parameter("clientId").value(clientId).notNull().longGreaterThanZero();
baseDataValidator.reset().parameter("groupId").value(groupId).mustBeBlankWhenParameterProvided("clientId", clientId);
}
@@ -93,7 +94,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
}
}
-
+
final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
baseDataValidator.reset().parameter("productId").value(productId).notNull().integerGreaterThanZero();
@@ -176,13 +177,15 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final LocalDate expectedDisbursementDate = fromApiJsonHelper.extractLocalDateNamed(expectedDisbursementDateParameterName, element);
baseDataValidator.reset().parameter(expectedDisbursementDateParameterName).value(expectedDisbursementDate).notNull();
- final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
- if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
- final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
- element);
- baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull()
- .notNull();
- }
+ // grace validation
+ final Integer graceOnPrincipalPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
+ baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount();
+
+ final Integer graceOnInterestPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
+ baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount();
+
+ final Integer graceOnInterestCharged = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
+ baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount();
final String interestChargedFromDateParameterName = "interestChargedFromDate";
if (fromApiJsonHelper.parameterExists(interestChargedFromDateParameterName, element)) {
@@ -192,6 +195,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
.notNull();
}
+ final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
+ if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
+ final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
+ element);
+ baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull()
+ .notNull();
+ }
+
final String inArrearsToleranceParameterName = "inArrearsTolerance";
if (fromApiJsonHelper.parameterExists(inArrearsToleranceParameterName, element)) {
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(inArrearsToleranceParameterName,
@@ -304,7 +315,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
final JsonElement element = fromApiJsonHelper.parse(json);
boolean atLeastOneParameterPassedForUpdate = false;
-
+
final String clientIdParameterName = "clientId";
if (fromApiJsonHelper.parameterExists(clientIdParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
@@ -459,12 +470,20 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(expectedDisbursementDateParameterName).value(expectedDisbursementDate).notNull();
}
- final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
- if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
- atLeastOneParameterPassedForUpdate = true;
- final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
- element);
- baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull();
+ // grace validation
+ if (fromApiJsonHelper.parameterExists("graceOnPrincipalPayment", element)) {
+ final Integer graceOnPrincipalPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
+ baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount();
+ }
+
+ if (fromApiJsonHelper.parameterExists("graceOnInterestPayment", element)) {
+ final Integer graceOnInterestPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
+ baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount();
+ }
+
+ if (fromApiJsonHelper.parameterExists("graceOnInterestCharged", element)) {
+ final Integer graceOnInterestCharged = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
+ baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount();
}
final String interestChargedFromDateParameterName = "interestChargedFromDate";
@@ -475,6 +494,14 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(interestChargedFromDateParameterName).value(interestChargedFromDate).ignoreIfNull();
}
+ final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
+ if (fromApiJsonHelper.parameterExists(repaymentsStartingFromDateParameterName, element)) {
+ atLeastOneParameterPassedForUpdate = true;
+ final LocalDate repaymentsStartingFromDate = fromApiJsonHelper.extractLocalDateNamed(repaymentsStartingFromDateParameterName,
+ element);
+ baseDataValidator.reset().parameter(repaymentsStartingFromDateParameterName).value(repaymentsStartingFromDate).ignoreIfNull();
+ }
+
final String submittedOnDateParameterName = "submittedOnDate";
if (fromApiJsonHelper.parameterExists(submittedOnDateParameterName, element)) {
atLeastOneParameterPassedForUpdate = true;
@@ -593,7 +620,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
-
+
public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct) {
final List dataValidationErrors = new ArrayList();
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
index 34f0e8069..06a634447 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
@@ -51,8 +51,8 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateExce
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
-import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationCommandFromApiJsonHelper;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanApplicationTransitionApiJsonValidator;
@@ -60,7 +60,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
-import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
+import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductDataValidator;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -81,7 +81,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
private final PlatformSecurityContext context;
private final FromJsonHelper fromJsonHelper;
private final LoanApplicationTransitionApiJsonValidator loanApplicationTransitionApiJsonValidator;
- private final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer;
+ private final LoanProductDataValidator loanProductCommandFromApiJsonDeserializer;
private final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer;
private final LoanRepository loanRepository;
private final NoteRepository noteRepository;
@@ -102,7 +102,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
final LoanApplicationTransitionApiJsonValidator loanApplicationTransitionApiJsonValidator,
final LoanApplicationCommandFromApiJsonHelper fromApiJsonDeserializer,
- final LoanProductCommandFromApiJsonDeserializer loanProductCommandFromApiJsonDeserializer, final AprCalculator aprCalculator,
+ final LoanProductDataValidator loanProductCommandFromApiJsonDeserializer, final AprCalculator aprCalculator,
final LoanAssembler loanAssembler, final LoanChargeAssembler loanChargeAssembler,
final CollateralAssembler loanCollateralAssembler, final LoanRepository loanRepository, final NoteRepository noteRepository,
final LoanScheduleCalculationPlatformService calculationPlatformService, final ClientRepositoryWrapper clientRepository,
@@ -294,7 +294,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json());
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);
- final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
+ final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
existingLoanApplication.updateLoanSchedule(loanSchedule);
}
@@ -536,14 +536,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final LocalDate edDate = command.localDateValueOfParameterNamed("expectedDisbursementDate");
if (edDate != null) {
- //Expected disbursement date should not be before Meeting start date
- if(edDate.isBefore(calendar.getStartDateLocalDate())){
+ // Expected disbursement date should not be before Meeting start
+ // date
+ if (edDate.isBefore(calendar.getStartDateLocalDate())) {
final String errorMessage = "Expected disbursement date '" + edDate.toString() + "' cannot be before meeting start date '"
+ calendar.getStartDate().toString() + "' ";
- throw new LoanApplicationDateException("disbursement.date.cannot.be.before.meeting.start.date", errorMessage, edDate, calendar.getStartDate());
+ throw new LoanApplicationDateException("disbursement.date.cannot.be.before.meeting.start.date", errorMessage, edDate,
+ calendar.getStartDate());
}
-
- //Disbursement date should fall on a meeting date
+
+ // Disbursement date should fall on a meeting date
if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), edDate)) {
final String errorMessage = "Expected disbursement date '" + edDate.toString() + "' does not fall on a meeting date.";
throw new NotValidRecurringDateException("loan.expected.disbursement.date", errorMessage, edDate.toString(),
@@ -553,18 +555,20 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
}
final LocalDate repStartingDate = command.localDateValueOfParameterNamed("repaymentsStartingFromDate");
-
+
if (repStartingDate != null) {
- //First repayment date should not be before Meeting date
- if(repStartingDate.isBefore(calendar.getStartDateLocalDate())){
- final String errorMessage = "First repayment date '" + repStartingDate.toString() + "' cannot be before meeting start date '"
- + calendar.getStartDate().toString() + "' ";
- throw new LoanApplicationDateException("first.repayment.date.cannot.be.before.meeting.start.date", errorMessage, repStartingDate, calendar.getStartDate());
+ // First repayment date should not be before Meeting date
+ if (repStartingDate.isBefore(calendar.getStartDateLocalDate())) {
+ final String errorMessage = "First repayment date '" + repStartingDate.toString()
+ + "' cannot be before meeting start date '" + calendar.getStartDate().toString() + "' ";
+ throw new LoanApplicationDateException("first.repayment.date.cannot.be.before.meeting.start.date", errorMessage,
+ repStartingDate, calendar.getStartDate());
}
-
+
if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), repStartingDate)) {
- final String errorMessage = "The First repayment date '" + repStartingDate.toString() + "' does not fall on a meeting date.";
+ final String errorMessage = "The First repayment date '" + repStartingDate.toString()
+ + "' does not fall on a meeting date.";
throw new NotValidRecurringDateException("loan.first.repayment.date", errorMessage, repStartingDate.toString(),
calendar.getTitle());
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
index 21d649072..b8d500a26 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java
@@ -5,7 +5,6 @@
*/
package org.mifosplatform.portfolio.loanaccount.service;
-import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -40,9 +39,11 @@ 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;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
+import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
@@ -131,22 +132,21 @@ public class LoanAssembler {
}
}
- final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
- final LoanSchedule loanSchedule = this.loanScheduleAssembler.fromJson(element, inArrearsTolerance);
-
Loan loanApplication = null;
Client client = null;
Group group = null;
-
+
+ final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
+
final String loanTypeParameterName = "loanType";
final String loanTypeStr = fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
final EnumOptionData loanType = LoanEnumerations.loanType(loanTypeStr);
-
+
if (clientId != null) {
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
- loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanSchedule, loanCharges, collateral);
+ loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral);
}
if (groupId != null) {
@@ -154,15 +154,15 @@ public class LoanAssembler {
if (group == null) { throw new GroupNotFoundException(groupId); }
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
- loanTransactionProcessingStrategy, loanSchedule, loanCharges);
+ loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges);
}
if (client != null && group != null) {
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); }
- loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
- loanTransactionProcessingStrategy, loanSchedule, loanCharges);
+ loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
+ loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges);
}
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
@@ -172,8 +172,11 @@ public class LoanAssembler {
loanApplication.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
this.loanRepaymentScheduleTransactionProcessorFactory);
- loanApplication
- .loanApplicationSubmittal(currentUser, loanSchedule, defaultLoanLifecycleStateMachine(), submittedOnDate, externalId);
+ final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
+ final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms, element);
+
+ loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(),
+ submittedOnDate, externalId);
return loanApplication;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 1212b2bfc..65cb2f02a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -95,7 +95,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final PaginationHelper paginationHelper = new PaginationHelper();
private final LoanMapper loaanLoanMapper = new LoanMapper();
-
@Autowired
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository,
@@ -402,6 +401,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " 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.principal_amount as principal, l.arrearstolerance_amount as inArrearsTolerance, l.number_of_repayments as numberOfRepayments, l.repay_every as repaymentEvery,"
+ + " l.grace_on_principal_periods as graceOnPrincipalPayment, l.grace_on_interest_periods as graceOnInterestPayment, l.grace_interest_free_periods as graceOnInterestCharged,"
+ " 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, "
+ " l.term_frequency as termFrequency, l.term_period_frequency_enum as termPeriodFrequencyType, "
@@ -550,6 +550,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
+ final Integer graceOnPrincipalPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnPrincipalPayment");
+ final Integer graceOnInterestPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestPayment");
+ final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged");
+
final Integer termFrequency = JdbcSupport.getInteger(rs, "termFrequency");
final Integer termPeriodFrequencyTypeInt = JdbcSupport.getInteger(rs, "termPeriodFrequencyType");
final EnumOptionData termPeriodFrequencyType = LoanEnumerations.termFrequencyType(termPeriodFrequencyTypeInt);
@@ -645,7 +649,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType,
numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, amortizationType,
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
- expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged);
+ expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
+ interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged);
}
}
@@ -864,7 +869,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId) {
-
context.authenticatedUser();
final LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 0102458e8..3ad2152e5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -62,6 +62,7 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
+import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
@@ -91,6 +92,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
+ private final LoanScheduleGeneratorFactory loanScheduleFactory;
private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@@ -103,7 +105,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
- final CalendarInstanceRepository calendarInstanceRepository,
+ final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
@@ -118,6 +120,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
+ this.loanScheduleFactory = loanScheduleFactory;
this.calendarInstanceRepository = calendarInstanceRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
}
@@ -152,8 +155,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
- loan.disburse(currentUser, command, applicationCurrency, existingTransactionIds, existingReversedTransactionIds, changes,
- paymentDetail);
+ loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
+ existingReversedTransactionIds, changes, paymentDetail);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
@@ -178,7 +181,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.build();
}
- private void validateDisbursementDate(LocalDate disbursementDate, Collection calendarInstances) {
+ private void validateDisbursementDate(final LocalDate disbursementDate, final Collection calendarInstances) {
if (!CollectionUtils.isEmpty(calendarInstances)) {
Calendar calendar = null;
for (CalendarInstance calendarInstance : calendarInstances) {
@@ -208,16 +211,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
+ /****
+ * TODO Vishwas: Pair with Ashok and re-factor collection sheet code-base
+ *****/
@Transactional
@Override
public Map bulkLoanDisbursal(final JsonCommand command, final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand) {
final AppUser currentUser = context.authenticatedUser();
- /****
- * TODO Vishwas: Pair with Ashok and re-factor collection sheet
- * code-base
- *****/
-
SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
Map changes = new LinkedHashMap();
if (disbursalCommand == null) { return changes; }
@@ -234,8 +235,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
- loan.disburse(currentUser, command, applicationCurrency, existingTransactionIds, existingReversedTransactionIds, changes,
- paymentDetail);
+ loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds,
+ existingReversedTransactionIds, changes, paymentDetail);
if (!changes.isEmpty()) {
this.loanRepository.save(loan);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java
index 32f0b7e45..cce082ab1 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -61,13 +61,14 @@ public class LoanProductsApiResource {
private final Set LOAN_PRODUCT_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "name", "description", "fundId",
"fundName", "currency", "principal", "minPrincipal", "maxPrincipal", "numberOfRepayments", "minNumberOfRepayments",
- "maxNumberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod",
- "maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType",
- "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
- "charges", "accountingRule", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions",
- "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions",
- "interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions",
- "accountingOptions", "accountingRuleOptions", "accountingMappingOptions"));
+ "maxNumberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "graceOnPrincipalPayment", "graceOnInterestPayment",
+ "graceOnInterestCharged", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod",
+ "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType",
+ "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule",
+ "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions", "currencyOptions",
+ "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions",
+ "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
+ "accountingRuleOptions", "accountingMappingOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java
index 1f91573e9..5791981c5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java
@@ -60,6 +60,9 @@ public class LoanProductData {
private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
+ private final Integer graceOnPrincipalPayment;
+ private final Integer graceOnInterestPayment;
+ private final Integer graceOnInterestCharged;
// charges
private final Collection charges;
@@ -112,6 +115,9 @@ public class LoanProductData {
final String fundName = null;
final Long transactionProcessingStrategyId = null;
final String transactionProcessingStrategyName = null;
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
final Collection charges = null;
final EnumOptionData accountingType = null;
@@ -119,7 +125,8 @@ public class LoanProductData {
minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod,
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
- transactionProcessingStrategyName, charges, accountingType);
+ transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
+ accountingType);
}
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
@@ -145,6 +152,10 @@ public class LoanProductData {
final String fundName = null;
final Long transactionProcessingStrategyId = null;
final String transactionProcessingStrategyName = null;
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
+
final Collection charges = null;
final EnumOptionData accountingType = null;
@@ -152,7 +163,8 @@ public class LoanProductData {
minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod,
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
- transactionProcessingStrategyName, charges, accountingType);
+ transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
+ accountingType);
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -167,6 +179,7 @@ public class LoanProductData {
final Integer numberOfRepayments = null;
final Integer minNumberOfRepayments = null;
final Integer maxNumberOfRepayments = null;
+
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal minInterestRatePerPeriod = null;
@@ -182,6 +195,11 @@ public class LoanProductData {
final String fundName = null;
final Long transactionProcessingStrategyId = null;
final String transactionProcessingStrategyName = null;
+
+ final Integer graceOnPrincipalPayment = null;
+ final Integer graceOnInterestPayment = null;
+ final Integer graceOnInterestCharged = null;
+
final Collection charges = null;
final EnumOptionData accountingType = AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE);
@@ -189,7 +207,8 @@ public class LoanProductData {
minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod,
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
- transactionProcessingStrategyName, charges, accountingType);
+ transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
+ accountingType);
}
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings,
@@ -210,6 +229,7 @@ public class LoanProductData {
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
+ final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final Collection charges, final EnumOptionData accountingType) {
this.id = id;
this.name = name;
@@ -222,6 +242,9 @@ public class LoanProductData {
this.numberOfRepayments = numberOfRepayments;
this.minNumberOfRepayments = minNumberOfRepayments;
this.maxNumberOfRepayments = maxNumberOfRepayments;
+ this.graceOnPrincipalPayment = graceOnPrincipalPayment;
+ this.graceOnInterestPayment = graceOnInterestPayment;
+ this.graceOnInterestCharged = graceOnInterestCharged;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
this.minInterestRatePerPeriod = minInterestRatePerPeriod;
@@ -308,6 +331,10 @@ public class LoanProductData {
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
}
+ this.graceOnPrincipalPayment = productData.graceOnPrincipalPayment;
+ this.graceOnInterestPayment = productData.graceOnInterestPayment;
+ this.graceOnInterestCharged = productData.graceOnInterestCharged;
+
this.amortizationTypeOptions = amortizationTypeOptions;
this.interestTypeOptions = interestTypeOptions;
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
@@ -418,6 +445,18 @@ public class LoanProductData {
return repaymentFrequencyType;
}
+ public Integer getGraceOnPrincipalPayment() {
+ return this.graceOnPrincipalPayment;
+ }
+
+ public Integer getGraceOnInterestPayment() {
+ return this.graceOnInterestPayment;
+ }
+
+ public Integer getGraceOnInterestCharged() {
+ return this.graceOnInterestCharged;
+ }
+
public EnumOptionData getInterestRateFrequencyType() {
return interestRateFrequencyType;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
index 1779a2f47..d9d894631 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java
@@ -104,12 +104,19 @@ public class LoanProduct extends AbstractPersistable {
final Integer minNumberOfRepayments = command.integerValueOfParameterNamed("minNumberOfRepayments");
final Integer maxNumberOfRepayments = command.integerValueOfParameterNamed("maxNumberOfRepayments");
final BigDecimal inArrearsTolerance = command.bigDecimalValueOfParameterNamed("inArrearsTolerance");
+
+ // grace details
+ final Integer graceOnPrincipalPayment = command.integerValueOfParameterNamed("graceOnPrincipalPayment");
+ final Integer graceOnInterestPayment = command.integerValueOfParameterNamed("graceOnInterestPayment");
+ final Integer graceOnInterestCharged = command.integerValueOfParameterNamed("graceOnInterestCharged");
+
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal,
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate,
interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments,
- minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType);
+ minNumberOfRepayments, maxNumberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
+ amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType);
}
protected LoanProduct() {
@@ -126,6 +133,7 @@ public class LoanProduct extends AbstractPersistable {
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
+ final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List charges,
final AccountingRuleType accountingRuleType) {
this.fund = fund;
@@ -143,7 +151,10 @@ public class LoanProduct extends AbstractPersistable {
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
- repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance);
+ repaymentFrequencyType, defaultNumberOfInstallments, graceOnPrincipalPayment, graceOnInterestPayment,
+ graceOnInterestCharged, amortizationMethod, inArrearsTolerance);
+
+ this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings();
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
@@ -274,6 +285,10 @@ public class LoanProduct extends AbstractPersistable {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
+ public PeriodFrequencyType getInterestPeriodFrequencyType() {
+ return this.loanProductRelatedDetail.getInterestPeriodFrequencyType();
+ }
+
public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints().getMinNominalInterestRatePerPeriod();
}
@@ -283,7 +298,7 @@ public class LoanProduct extends AbstractPersistable {
}
public Integer getNumberOfRepayments() {
- return this.loanProductRelatedDetail().getNumberOfRepayments();
+ return this.loanProductRelatedDetail.getNumberOfRepayments();
}
public Integer getMinNumberOfRepayments() {
@@ -294,10 +309,6 @@ public class LoanProduct extends AbstractPersistable {
return this.loanProductMinMaxConstraints().getMaxNumberOfRepayments();
}
- public LoanProductRelatedDetail loanProductRelatedDetail() {
- return this.loanProductRelatedDetail;
- }
-
public LoanProductMinMaxConstraints loanProductMinMaxConstraints() {
// If all min and max fields are null then loanProductMinMaxConstraints
// initialising to null
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
index 94d4a237e..f114bf3fb 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java
@@ -6,7 +6,9 @@
package org.mifosplatform.portfolio.loanproduct.domain;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import javax.persistence.Column;
@@ -16,6 +18,9 @@ import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
+import org.mifosplatform.infrastructure.core.data.ApiParameterError;
+import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
+import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
@@ -33,7 +38,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = false)
private BigDecimal principal;
-
+
@Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = false)
private BigDecimal nominalInterestRatePerPeriod;
@@ -69,7 +74,16 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "number_of_repayments", nullable = false)
private Integer numberOfRepayments;
-
+
+ @Column(name = "grace_on_principal_periods", nullable = true)
+ private Integer graceOnPrincipalPayment;
+
+ @Column(name = "grace_on_interest_periods", nullable = true)
+ private Integer graceOnInterestPayment;
+
+ @Column(name = "grace_interest_free_periods", nullable = true)
+ private Integer graceOnInterestCharged;
+
// FIXME - move away form JPA ordinal use for enums using just integer -
// requires sql patch for existing users of software.
@Enumerated(EnumType.ORDINAL)
@@ -84,11 +98,13 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repaymentEvery,
final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer numberOfRepayments,
+ final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentPeriodFrequencyType,
- numberOfRepayments, amortizationMethod, inArrearsTolerance);
+ numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
+ inArrearsTolerance);
}
protected LoanProductRelatedDetail() {
@@ -96,10 +112,11 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
}
public LoanProductRelatedDetail(final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
- final BigDecimal defaultNominalInterestRatePerPeriod,final PeriodFrequencyType interestPeriodFrequencyType,
+ final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfRepayments,
+ final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance) {
this.currency = currency;
this.principal = defaultPrincipal;
@@ -111,6 +128,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.repayEvery = repayEvery;
this.repaymentPeriodFrequencyType = repaymentFrequencyType;
this.numberOfRepayments = defaultNumberOfRepayments;
+ this.graceOnPrincipalPayment = defaultToNullIfZero(graceOnPrincipalPayment);
+ this.graceOnInterestPayment = defaultToNullIfZero(graceOnInterestPayment);
+ this.graceOnInterestCharged = defaultToNullIfZero(graceOnInterestCharged);
this.amortizationMethod = amortizationMethod;
if (inArrearsTolerance != null && BigDecimal.ZERO.compareTo(inArrearsTolerance) == 0) {
this.inArrearsTolerance = null;
@@ -119,6 +139,14 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
}
}
+ private Integer defaultToNullIfZero(final Integer value) {
+ Integer defaultTo = value;
+ if (value != null && Integer.valueOf(0).equals(value)) {
+ defaultTo = null;
+ }
+ return defaultTo;
+ }
+
public MonetaryCurrency getCurrency() {
return this.currency.copy();
}
@@ -126,7 +154,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public Money getPrincipal() {
return Money.of(this.currency, this.principal);
}
-
+
public Money getInArrearsTolerance() {
return Money.of(this.currency, this.inArrearsTolerance);
}
@@ -165,7 +193,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
public Integer getNumberOfRepayments() {
return numberOfRepayments;
}
-
+
public AmortizationMethod getAmortizationMethod() {
return amortizationMethod;
}
@@ -231,7 +259,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
actualChanges.put("locale", localeAsInput);
this.repaymentPeriodFrequencyType = PeriodFrequencyType.fromInt(newValue);
}
-
+
final String numberOfRepaymentsParamName = "numberOfRepayments";
if (command.isChangeInIntegerParameterNamed(numberOfRepaymentsParamName, this.numberOfRepayments)) {
final Integer newValue = command.integerValueOfParameterNamed(numberOfRepaymentsParamName);
@@ -255,7 +283,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
actualChanges.put("locale", localeAsInput);
this.inArrearsTolerance = newValue;
}
-
+
final String interestRatePerPeriodParamName = "interestRatePerPeriod";
if (command.isChangeInBigDecimalParameterNamed(interestRatePerPeriodParamName, this.nominalInterestRatePerPeriod)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(interestRatePerPeriodParamName);
@@ -291,9 +319,65 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(newValue);
}
+ final String graceOnPrincipalPaymentParamName = "graceOnPrincipalPayment";
+ if (command.isChangeInIntegerParameterNamed(graceOnPrincipalPaymentParamName, this.graceOnPrincipalPayment)) {
+ final Integer newValue = command.integerValueOfParameterNamed(graceOnPrincipalPaymentParamName);
+ actualChanges.put(graceOnPrincipalPaymentParamName, newValue);
+ actualChanges.put("locale", localeAsInput);
+ this.graceOnPrincipalPayment = newValue;
+ }
+
+ final String graceOnInterestPaymentParamName = "graceOnInterestPayment";
+ if (command.isChangeInIntegerParameterNamed(graceOnInterestPaymentParamName, this.graceOnInterestPayment)) {
+ final Integer newValue = command.integerValueOfParameterNamed(graceOnInterestPaymentParamName);
+ actualChanges.put(graceOnInterestPaymentParamName, newValue);
+ actualChanges.put("locale", localeAsInput);
+ this.graceOnInterestPayment = newValue;
+ }
+
+ final String graceOnInterestChargedParamName = "graceOnInterestCharged";
+ if (command.isChangeInIntegerParameterNamed(graceOnInterestChargedParamName, this.graceOnInterestCharged)) {
+ final Integer newValue = command.integerValueOfParameterNamed(graceOnInterestChargedParamName);
+ actualChanges.put(graceOnInterestChargedParamName, newValue);
+ actualChanges.put("locale", localeAsInput);
+ this.graceOnInterestCharged = newValue;
+ }
+
+ validateRepaymentPeriodWithGraceSettings();
+
return actualChanges;
}
+ public void validateRepaymentPeriodWithGraceSettings() {
+ final List dataValidationErrors = new ArrayList();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
+
+ if (this.numberOfRepayments <= defaultToZeroIfNull(this.graceOnPrincipalPayment)) {
+ baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment)
+ .failWithCode(".mustBeLessThan.numberOfRepayments");
+ }
+
+ if (this.numberOfRepayments <= defaultToZeroIfNull(this.graceOnInterestPayment)) {
+ baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment)
+ .failWithCode(".mustBeLessThan.numberOfRepayments");
+ }
+
+ if (this.numberOfRepayments < defaultToZeroIfNull(this.graceOnInterestCharged)) {
+ baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged)
+ .failWithCode(".mustBeLessThan.numberOfRepayments");
+ }
+
+ if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
+ }
+
+ private Integer defaultToZeroIfNull(final Integer value) {
+ Integer result = value;
+ if (value == null) {
+ result = Integer.valueOf(0);
+ }
+ return result;
+ }
+
private void updateInterestRateDerivedFields(final AprCalculator aprCalculator) {
this.annualNominalInterestRate = aprCalculator.calculateFrom(this.interestPeriodFrequencyType, this.nominalInterestRatePerPeriod);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
similarity index 95%
rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java
rename to mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index 9cbd5520b..f0a77c00d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductCommandFromApiJsonDeserializer.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -16,8 +16,8 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
-import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
+import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -33,7 +33,7 @@ import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Component
-public final class LoanProductCommandFromApiJsonDeserializer {
+public final class LoanProductDataValidator {
/**
* The parameters supported for this command.
@@ -42,7 +42,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
"currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments",
"minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
- "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
+ "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
+ "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -53,7 +54,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
- public LoanProductCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
+ public LoanProductDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
@@ -218,6 +219,16 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
+ // grace validation
+ final Integer graceOnPrincipalPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
+ baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount();
+
+ final Integer graceOnInterestPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
+ baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount();
+
+ final Integer graceOnInterestCharged = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
+ baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount();
+
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -396,6 +407,23 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.integerGreaterThanZero();
}
+ // grace validation
+ if (fromApiJsonHelper.parameterExists("graceOnPrincipalPayment", element)) {
+ final Integer graceOnPrincipalPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
+ baseDataValidator.reset().parameter("graceOnPrincipalPayment").value(graceOnPrincipalPayment).zeroOrPositiveAmount();
+ }
+
+ if (fromApiJsonHelper.parameterExists("graceOnInterestPayment", element)) {
+ final Integer graceOnInterestPayment = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
+ baseDataValidator.reset().parameter("graceOnInterestPayment").value(graceOnInterestPayment).zeroOrPositiveAmount();
+ }
+
+ if (fromApiJsonHelper.parameterExists("graceOnInterestCharged", element)) {
+ final Integer graceOnInterestCharged = fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element);
+ baseDataValidator.reset().parameter("graceOnInterestCharged").value(graceOnInterestCharged).zeroOrPositiveAmount();
+ }
+
+ //
if (fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
@@ -473,11 +501,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
- /**
+ /*
* Validation for advanced accounting options
- *
- * @param baseDataValidator
- * @param element
*/
private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (fromApiJsonHelper.parameterExists(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 71dad3c11..f702558f8 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -101,6 +101,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
+ + "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,"
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
+ "lp.accounting_type as accountingType, "
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol "
@@ -140,6 +141,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final Integer maxNumberOfRepayments = JdbcSupport.getInteger(rs, "maxNumberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
+ final Integer graceOnPrincipalPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnPrincipalPayment");
+ final Integer graceOnInterestPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestPayment");
+ final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged");
+
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId);
@@ -168,7 +173,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
- transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType);
+ transactionStrategyId, transactionStrategyName, graceOnPrincipalPayment, graceOnInterestPayment,
+ graceOnInterestCharged, this.charges, accountingRuleType);
}
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
index 379df3185..75de974fe 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java
@@ -28,7 +28,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
-import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
+import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductDataValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -44,7 +44,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final static Logger logger = LoggerFactory.getLogger(LoanProductWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context;
- private final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+ private final LoanProductDataValidator fromApiJsonDeserializer;
private final LoanProductRepository loanProductRepository;
private final AprCalculator aprCalculator;
private final FundRepository fundRepository;
@@ -54,7 +54,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
@Autowired
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
- final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
+ final LoanProductDataValidator fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepositoryWrapper chargeRepository,
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/InterestCompoundingPeriodSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/InterestCompoundingPeriodSummary.java
index eff523c7a..a21253e49 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/InterestCompoundingPeriodSummary.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/InterestCompoundingPeriodSummary.java
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.math.MathContext;
import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
index cb0af6e76..f1a3ec3fb 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
@@ -46,6 +46,7 @@ import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
+import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDailyBalance.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDailyBalance.java
index de2241e57..96a6b13ee 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDailyBalance.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDailyBalance.java
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
+import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
index d4eff548a..4851d5c8a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java
@@ -19,6 +19,7 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.joda.time.LocalDate;
+import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsAccountStateTransitionException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsAccountStateTransitionException.java
index e6071f8f2..0d10e696e 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsAccountStateTransitionException.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsAccountStateTransitionException.java
@@ -1,3 +1,8 @@
+/**
+ * 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.savings.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
diff --git a/mifosng-provider/src/main/resources/META-INF/spring/scheduler.xml b/mifosng-provider/src/main/resources/META-INF/spring/scheduler.xml
index 5afe7d5ff..03411a951 100644
--- a/mifosng-provider/src/main/resources/META-INF/spring/scheduler.xml
+++ b/mifosng-provider/src/main/resources/META-INF/spring/scheduler.xml
@@ -1,4 +1,11 @@
+