mifosx-58: wip - refactoring api for loan schedule data - introduced new schedule in prep for supporting charges.
This commit is contained in:
@@ -37,7 +37,6 @@ import org.mifosng.platform.api.data.LoanChargeData;
|
||||
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||
import org.mifosng.platform.api.data.LoanProductLookup;
|
||||
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.LoanScheduleData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
@@ -273,49 +272,50 @@ public class LoansApiResource {
|
||||
|
||||
if (is(commandParam, "calculateLoanSchedule")) {
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
||||
NewLoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(calculateLoanScheduleCommand);
|
||||
|
||||
final Set<String> typicalLoanScheduleResponseParameters = new HashSet<String>(
|
||||
Arrays.asList("periods", "cumulativePrincipalDisbursed"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalLoanScheduleResponseParameters);
|
||||
}
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
return this.apiJsonSerializerService.serializeLoanScheduleDataToJson(prettyPrint, responseParameters, loanSchedule);
|
||||
return calculateLoanSchedule(uriInfo, calculateLoanScheduleCommand);
|
||||
}
|
||||
|
||||
final EntityIdentifier identifier = this.loanWritePlatformService.submitLoanApplication(command);
|
||||
|
||||
return this.apiJsonSerializerService.serializeEntityIdentifier(identifier);
|
||||
}
|
||||
|
||||
private String calculateLoanSchedule(final UriInfo uriInfo, final CalculateLoanScheduleCommand command) {
|
||||
|
||||
final NewLoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command);
|
||||
|
||||
final Set<String> typicalLoanScheduleResponseParameters = new HashSet<String>(
|
||||
Arrays.asList("periods", "cumulativePrincipalDisbursed"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalLoanScheduleResponseParameters);
|
||||
}
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
return this.apiJsonSerializerService.serializeLoanScheduleDataToJson(prettyPrint, responseParameters, loanSchedule);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{loanId}")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
public Response modifyLoanApplication(
|
||||
public String modifyLoanApplication(
|
||||
@PathParam("loanId") final Long loanId,
|
||||
@QueryParam("command") final String commandParam,
|
||||
// @QueryParam("command") final String commandParam,
|
||||
// @Context final UriInfo uriInfo,
|
||||
final String jsonRequestBody) {
|
||||
|
||||
final LoanApplicationCommand command = this.apiDataConversionService.convertJsonToLoanApplicationCommand(loanId, jsonRequestBody);
|
||||
|
||||
// for now just auto generating the loan schedule and setting support
|
||||
// for 'manual' loan schedule creation later.
|
||||
// command.setLoanSchedule(loanSchedule);
|
||||
// if (is(commandParam, "calculateLoanSchedule")) {
|
||||
// CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
||||
// return calculateLoanSchedule(uriInfo, calculateLoanScheduleCommand);
|
||||
// }
|
||||
|
||||
if (is(commandParam, "calculateLoanSchedule")) {
|
||||
final CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
||||
final LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand);
|
||||
return Response.ok().entity(loanSchedule).build();
|
||||
}
|
||||
final EntityIdentifier identifier = this.loanWritePlatformService.modifyLoanApplication(command);
|
||||
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.modifyLoanApplication(command);
|
||||
|
||||
return Response.ok().entity(identifier).build();
|
||||
return this.apiJsonSerializerService.serializeEntityIdentifier(identifier);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@@ -324,8 +324,7 @@ public class LoansApiResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
|
||||
|
||||
EntityIdentifier identifier = this.loanWritePlatformService
|
||||
.deleteLoan(loanId);
|
||||
EntityIdentifier identifier = this.loanWritePlatformService.deleteLoan(loanId);
|
||||
|
||||
return Response.ok().entity(identifier).build();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.mifosng.platform.api;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
|
||||
/**
|
||||
@@ -16,10 +17,18 @@ import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
@SuppressWarnings("unused")
|
||||
public class NewLoanScheduleData {
|
||||
|
||||
private final Integer loanTermInDays;
|
||||
|
||||
/**
|
||||
* The currency associated with all monetary values in loan schedule.
|
||||
*/
|
||||
private final CurrencyData currency;
|
||||
|
||||
/**
|
||||
* The cumulative total of all money (principal) disbursed to the loan applicant.
|
||||
*/
|
||||
private final BigDecimal cumulativePrincipalDisbursed;
|
||||
private final BigDecimal cumulativePrincipalDue;
|
||||
private final BigDecimal cumulativePrincipalPaid;
|
||||
private final BigDecimal cumulativePrincipalOutstanding;
|
||||
|
||||
@@ -55,13 +64,16 @@ public class NewLoanScheduleData {
|
||||
private final BigDecimal totalInArrears;
|
||||
|
||||
/**
|
||||
* <code>periods</code>
|
||||
* <code>periods</code> is collection of data objects containing specific
|
||||
* information to each period of the loan schedule including disbursment and
|
||||
* repayment information.
|
||||
*/
|
||||
private final Collection<LoanSchedulePeriodData> periods;
|
||||
|
||||
public NewLoanScheduleData() {
|
||||
this.cumulativePrincipalDisbursed = null;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalDue = null;
|
||||
this.cumulativePrincipalOutstanding = null;
|
||||
|
||||
this.cumulativeInterestExpected = null;
|
||||
@@ -78,28 +90,52 @@ public class NewLoanScheduleData {
|
||||
this.totalExpectedRepayment = null;
|
||||
this.totalInArrears = null;
|
||||
|
||||
this.currency = null;
|
||||
this.periods = null;
|
||||
this.loanTermInDays = null;
|
||||
}
|
||||
|
||||
public NewLoanScheduleData(final Collection<LoanSchedulePeriodData> periods) {
|
||||
public NewLoanScheduleData(
|
||||
final CurrencyData currency,
|
||||
final Collection<LoanSchedulePeriodData> periods,
|
||||
final Integer loanTermInDays,
|
||||
final BigDecimal cumulativePrincipalDisbursed,
|
||||
final BigDecimal cumulativePrincipalDue,
|
||||
final BigDecimal cumulativePrincipalOutstanding,
|
||||
final BigDecimal cumulativeInterestExpected,
|
||||
final BigDecimal cumulativeChargesToDate,
|
||||
final BigDecimal totalExpectedRepayment) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
|
||||
this.cumulativePrincipalDisbursed = null;
|
||||
this.cumulativePrincipalDisbursed = cumulativePrincipalDisbursed;
|
||||
this.cumulativePrincipalDue = cumulativePrincipalDue;
|
||||
this.cumulativePrincipalPaid = null;
|
||||
this.cumulativePrincipalOutstanding = null;
|
||||
this.cumulativePrincipalOutstanding = cumulativePrincipalOutstanding;
|
||||
|
||||
this.cumulativeInterestExpected = null;
|
||||
this.cumulativeInterestExpected = cumulativeInterestExpected;
|
||||
this.cumulativeInterestPaid = null;
|
||||
this.cumulativeInterestWaived = null;
|
||||
this.cumulativeInterestOutstanding = null;
|
||||
|
||||
this.cumulativeChargesToDate = null;
|
||||
this.cumulativeChargesToDate = cumulativeChargesToDate;
|
||||
this.cumulativeChargesPaid = null;
|
||||
this.cumulativeChargesOutstanding = null;
|
||||
|
||||
this.totalCostOfLoan = null;
|
||||
if (cumulativeChargesToDate != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeChargesToDate);
|
||||
} else if (cumulativeInterestExpected != null) {
|
||||
this.totalCostOfLoan = cumulativeInterestExpected;
|
||||
} else {
|
||||
this.totalCostOfLoan = null;
|
||||
}
|
||||
|
||||
this.totalExpectedRepayment = null;
|
||||
this.totalExpectedRepayment = totalExpectedRepayment;
|
||||
this.totalInArrears = null;
|
||||
}
|
||||
|
||||
public Collection<LoanSchedulePeriodData> getPeriods() {
|
||||
return periods;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.mifosng.platform.api.data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Deprecated
|
||||
public class LoanSchedule {
|
||||
|
||||
private List<ScheduledLoanInstallment> scheduledLoanInstallments = new ArrayList<ScheduledLoanInstallment>();
|
||||
|
||||
+24
@@ -111,4 +111,28 @@ public class LoanSchedulePeriodData {
|
||||
|
||||
this.totalDueForPeriod = totalDueForPeriod;
|
||||
}
|
||||
|
||||
public boolean isRepaymentPeriod() {
|
||||
boolean isRepaymentPeriod = false;
|
||||
if (principalDue != null && this.interestDue != null) {
|
||||
isRepaymentPeriod = BigDecimal.ZERO.compareTo(this.principalDue) == -1 || BigDecimal.ZERO.compareTo(this.interestDue) == -1;
|
||||
}
|
||||
return isRepaymentPeriod;
|
||||
}
|
||||
|
||||
public Integer periodNumber() {
|
||||
return this.period;
|
||||
}
|
||||
|
||||
public LocalDate periodDueDate() {
|
||||
return this.dueDate;
|
||||
}
|
||||
|
||||
public BigDecimal principalDue() {
|
||||
return this.principalDue;
|
||||
}
|
||||
|
||||
public BigDecimal interestDue() {
|
||||
return this.interestDue;
|
||||
}
|
||||
}
|
||||
+1
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@Deprecated
|
||||
public class ScheduledLoanInstallment {
|
||||
|
||||
private Integer installmentNumber;
|
||||
|
||||
+36
-38
@@ -439,56 +439,54 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
|
||||
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
|
||||
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
Type typeOfMap = new TypeToken<Map<String, Object>>(){}.getType();
|
||||
Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
Set<String> supportedParams = new HashSet<String>(
|
||||
Arrays.asList("clientId", "productId", "externalId", "fundId", "transactionProcessingStrategyId",
|
||||
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments",
|
||||
"loanTermFrequency", "loanTermFrequencyType", "charges",
|
||||
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
|
||||
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
|
||||
"locale", "dateFormat", "loanOfficerId")
|
||||
Arrays.asList("clientId", "productId", "externalId", "fundId", "transactionProcessingStrategyId",
|
||||
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments",
|
||||
"loanTermFrequency", "loanTermFrequencyType", "charges",
|
||||
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
|
||||
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
|
||||
"locale", "dateFormat", "loanOfficerId", "id")
|
||||
);
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
|
||||
Set<String> modifiedParameters = new HashSet<String>();
|
||||
|
||||
Long clientId = extractLongParameter("clientId", requestMap, modifiedParameters);
|
||||
Long productId = extractLongParameter("productId", requestMap, modifiedParameters);
|
||||
Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters);
|
||||
Long loanOfficerId = extractLongParameter("loanOfficerId", requestMap, modifiedParameters);
|
||||
Long transactionProcessingStrategyId = extractLongParameter("transactionProcessingStrategyId", requestMap, modifiedParameters);
|
||||
String externalId = extractStringParameter("externalId", requestMap, modifiedParameters);
|
||||
|
||||
BigDecimal principal = extractBigDecimalParameter("principal", requestMap, modifiedParameters);
|
||||
BigDecimal inArrearsToleranceValue = extractBigDecimalParameter("inArrearsTolerance", requestMap, modifiedParameters);
|
||||
BigDecimal interestRatePerPeriod = extractBigDecimalParameter("interestRatePerPeriod", requestMap, modifiedParameters);
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonElement element = parser.parse(json);
|
||||
JsonParserHelper helper = new JsonParserHelper();
|
||||
|
||||
Integer repaymentEvery = extractIntegerParameter("repaymentEvery", requestMap, modifiedParameters);
|
||||
Integer numberOfRepayments = extractIntegerParameter("numberOfRepayments", requestMap, modifiedParameters);
|
||||
Integer repaymentFrequencyType = extractIntegerParameter("repaymentFrequencyType", requestMap, modifiedParameters);
|
||||
final Long clientId = helper.extractLongNamed("clientId", element, modifiedParameters);
|
||||
final Long productId = helper.extractLongNamed("productId", element, modifiedParameters);
|
||||
final Long fundId = helper.extractLongNamed("fundId", element, modifiedParameters);
|
||||
final Long loanOfficerId = helper.extractLongNamed("loanOfficerId", element, modifiedParameters);
|
||||
final Long transactionProcessingStrategyId = helper.extractLongNamed("transactionProcessingStrategyId", element, modifiedParameters);
|
||||
final String externalId = helper.extractStringNamed("externalId", element, modifiedParameters);
|
||||
final BigDecimal principal = helper.extractBigDecimalNamed("principal", element, modifiedParameters);
|
||||
final BigDecimal inArrearsToleranceValue = helper.extractBigDecimalNamed("inArrearsTolerance", element, modifiedParameters);
|
||||
final BigDecimal interestRatePerPeriod = helper.extractBigDecimalNamed("interestRatePerPeriod", element, modifiedParameters);
|
||||
|
||||
Integer loanTermFrequency = extractIntegerParameter("loanTermFrequency", requestMap, modifiedParameters);
|
||||
Integer loanTermFrequencyType = extractIntegerParameter("loanTermFrequencyType", requestMap, modifiedParameters);
|
||||
final Integer repaymentEvery = helper.extractIntegerNamed("repaymentEvery", element, modifiedParameters);
|
||||
final Integer numberOfRepayments = helper.extractIntegerNamed("numberOfRepayments", element, modifiedParameters);
|
||||
final Integer repaymentFrequencyType = helper.extractIntegerNamed("repaymentFrequencyType", element, modifiedParameters);
|
||||
final Integer loanTermFrequency = helper.extractIntegerNamed("loanTermFrequency", element, modifiedParameters);
|
||||
final Integer loanTermFrequencyType = helper.extractIntegerNamed("loanTermFrequencyType", element, modifiedParameters);
|
||||
final Integer interestRateFrequencyType = helper.extractIntegerNamed("interestRateFrequencyType", element, modifiedParameters);
|
||||
final Integer amortizationType = helper.extractIntegerNamed("amortizationType", element, modifiedParameters);
|
||||
final Integer interestType = helper.extractIntegerNamed("interestType", element, modifiedParameters);
|
||||
final Integer interestCalculationPeriodType = helper.extractIntegerNamed("interestCalculationPeriodType", element, modifiedParameters);
|
||||
|
||||
Integer interestRateFrequencyTypeValue = extractIntegerParameter("interestRateFrequencyType", requestMap, modifiedParameters);
|
||||
Integer amortizationTypeValue = extractIntegerParameter("amortizationType", requestMap, modifiedParameters);
|
||||
Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters);
|
||||
Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters);
|
||||
final LocalDate expectedDisbursementDate = helper.extractLocalDateNamed("expectedDisbursementDate", element, modifiedParameters);
|
||||
final LocalDate repaymentsStartingFromDate = helper.extractLocalDateNamed("repaymentsStartingFromDate", element, modifiedParameters);
|
||||
final LocalDate interestChargedFromDate = helper.extractLocalDateNamed("interestChargedFromDate", element, modifiedParameters);
|
||||
final LocalDate submittedOnDate = helper.extractLocalDateNamed("submittedOnDate", element, modifiedParameters);
|
||||
|
||||
LocalDate expectedDisbursementDate = extractLocalDateParameter("expectedDisbursementDate", requestMap, modifiedParameters);
|
||||
LocalDate repaymentsStartingFromDate = extractLocalDateParameter("repaymentsStartingFromDate", requestMap, modifiedParameters);
|
||||
LocalDate interestChargedFromDate = extractLocalDateParameter("interestChargedFromDate", requestMap, modifiedParameters);
|
||||
LocalDate submittedOnDate = extractLocalDateParameter("submittedOnDate", requestMap, modifiedParameters);
|
||||
final String submittedOnNote = helper.extractStringNamed("submittedOnNote", element, modifiedParameters);
|
||||
|
||||
String submittedOnNote = extractStringParameter("submittedOnNote", requestMap, modifiedParameters);
|
||||
|
||||
JsonParser parser = new JsonParser();
|
||||
|
||||
LoanChargeCommand[] charges = null;
|
||||
JsonElement element = parser.parse(json);
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has("charges") && object.get("charges").isJsonArray()) {
|
||||
@@ -518,8 +516,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
resourceIdentifier, clientId, productId, externalId, fundId, transactionProcessingStrategyId,
|
||||
submittedOnDate, submittedOnNote,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
|
||||
principal, interestRatePerPeriod, interestRateFrequencyTypeValue, interestTypeValue, interestCalculationPeriodTypeValue,
|
||||
repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationTypeValue,
|
||||
principal, interestRatePerPeriod, interestRateFrequencyType, interestType, interestCalculationPeriodType,
|
||||
repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationType,
|
||||
loanTermFrequency, loanTermFrequencyType,
|
||||
inArrearsToleranceValue, charges,loanOfficerId);
|
||||
}
|
||||
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
package org.mifosng.platform.api.infrastructure;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.mifosng.platform.api.data.ApiParameterError;
|
||||
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.format.number.NumberFormatter;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
/**
|
||||
* Helper class to extract values of json named attributes.
|
||||
*/
|
||||
public class JsonParserHelper {
|
||||
|
||||
public Long extractLongNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
|
||||
Long longValue = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
final String stringValue = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(stringValue)) {
|
||||
longValue = Long.valueOf(stringValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return longValue;
|
||||
}
|
||||
|
||||
public String extractStringNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
|
||||
String stringValue = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
if (StringUtils.isNotBlank(stringValue)) {
|
||||
stringValue = primitive.getAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
public BigDecimal extractBigDecimalNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
|
||||
BigDecimal value = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
value = convertFrom(valueAsString, parameterName, extractLocaleValue(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public Integer extractIntegerNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
|
||||
Integer value = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
value = convertToInteger(valueAsString, parameterName, extractLocaleValue(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, final Set<String> modifiedParameters) {
|
||||
LocalDate value = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
|
||||
String dateFormat = null;
|
||||
final String dateFormatParameter = "dateFormat";
|
||||
if (object.has(dateFormatParameter) && object.get(dateFormatParameter).isJsonPrimitive()) {
|
||||
JsonPrimitive primitive = object.get(dateFormatParameter).getAsJsonPrimitive();
|
||||
dateFormat = primitive.getAsString();
|
||||
}
|
||||
|
||||
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
final String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
value = convertFrom(valueAsString, parameterName, dateFormat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private LocalDate convertFrom(final String dateAsString, final String parameterName, final String dateFormat) {
|
||||
|
||||
if (StringUtils.isBlank(dateFormat)) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
String defaultMessage = new StringBuilder("The parameter '" + parameterName + "' requires a 'dateFormat' parameter to be passed with it.").toString();
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.dateFormat.parameter", defaultMessage, parameterName);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
LocalDate eventLocalDate = null;
|
||||
if (StringUtils.isNotBlank(dateAsString)) {
|
||||
try {
|
||||
Locale locale = LocaleContextHolder.getLocale();
|
||||
eventLocalDate = DateTimeFormat.forPattern(dateFormat)
|
||||
.withLocale(locale)
|
||||
.parseLocalDate(dateAsString.toLowerCase(locale));
|
||||
} catch (IllegalArgumentException e) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
ApiParameterError error = ApiParameterError
|
||||
.parameterError(
|
||||
"validation.msg.invalid.date.format",
|
||||
"The parameter "
|
||||
+ parameterName
|
||||
+ " is invalid based on the dateFormat provided:"
|
||||
+ dateFormat, parameterName,
|
||||
dateAsString, dateFormat);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
return eventLocalDate;
|
||||
}
|
||||
|
||||
private BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
|
||||
|
||||
if (clientApplicationLocale == null) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
String defaultMessage = new StringBuilder("The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString();
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
try {
|
||||
BigDecimal number = null;
|
||||
|
||||
if (StringUtils.isNotBlank(numericalValueFormatted)) {
|
||||
|
||||
String source = numericalValueFormatted.trim();
|
||||
|
||||
NumberFormat format = NumberFormat.getNumberInstance(clientApplicationLocale);
|
||||
DecimalFormat df = (DecimalFormat) format;
|
||||
DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
|
||||
// http://bugs.sun.com/view_bug.do?bug_id=4510618
|
||||
char groupingSeparator = symbols.getGroupingSeparator();
|
||||
if (groupingSeparator == '\u00a0') {
|
||||
source = source.replaceAll(" ", Character.toString('\u00a0'));
|
||||
}
|
||||
|
||||
NumberFormatter numberFormatter = new NumberFormatter();
|
||||
Number parsedNumber = numberFormatter.parse(source, clientApplicationLocale);
|
||||
number = BigDecimal.valueOf(Double.valueOf(parsedNumber.doubleValue()));
|
||||
}
|
||||
|
||||
return number;
|
||||
} catch (ParseException e) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
"validation.msg.invalid.decimal.format", "The parameter "
|
||||
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid decimal value for provided locale of ["
|
||||
+ clientApplicationLocale.toString() + "].",
|
||||
parameterName, numericalValueFormatted,
|
||||
clientApplicationLocale);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer convertToInteger(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
|
||||
|
||||
if (clientApplicationLocale == null) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
String defaultMessage = new StringBuilder("The parameter '" + parameterName + "' requires a 'locale' parameter to be passed with it.").toString();
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.missing.locale.parameter", defaultMessage, parameterName);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
try {
|
||||
Integer number = null;
|
||||
|
||||
if (StringUtils.isNotBlank(numericalValueFormatted)) {
|
||||
|
||||
String source = numericalValueFormatted.trim();
|
||||
|
||||
NumberFormat format = NumberFormat.getInstance(clientApplicationLocale);
|
||||
DecimalFormat df = (DecimalFormat) format;
|
||||
DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
|
||||
df.setParseBigDecimal(true);
|
||||
|
||||
// http://bugs.sun.com/view_bug.do?bug_id=4510618
|
||||
char groupingSeparator = symbols.getGroupingSeparator();
|
||||
if (groupingSeparator == '\u00a0') {
|
||||
source = source.replaceAll(" ", Character.toString('\u00a0'));
|
||||
}
|
||||
|
||||
Number parsedNumber = df.parse(source);
|
||||
|
||||
double parsedNumberDouble = parsedNumber.doubleValue();
|
||||
int parsedNumberInteger = parsedNumber.intValue();
|
||||
|
||||
if (source.contains(Character.toString(symbols.getDecimalSeparator()))) {
|
||||
throw new ParseException(source, 0);
|
||||
}
|
||||
|
||||
if (!Double.valueOf(parsedNumberDouble).equals(Double.valueOf(Integer.valueOf(parsedNumberInteger)))) {
|
||||
throw new ParseException(source, 0);
|
||||
}
|
||||
|
||||
number = parsedNumber.intValue();
|
||||
}
|
||||
|
||||
return number;
|
||||
} catch (ParseException e) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
ApiParameterError error = ApiParameterError.parameterError(
|
||||
"validation.msg.invalid.integer.format", "The parameter "
|
||||
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid integer value for provided locale of ["
|
||||
+ clientApplicationLocale.toString() + "].",
|
||||
parameterName, numericalValueFormatted,
|
||||
clientApplicationLocale);
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private Locale localeFromString(final String localeAsString) {
|
||||
|
||||
if (StringUtils.isBlank(localeAsString)) {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", "The parameter locale is invalid. It cannot be blank.", "locale");
|
||||
dataValidationErrors.add(error);
|
||||
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
String languageCode = "";
|
||||
String courntryCode = "";
|
||||
String variantCode = "";
|
||||
|
||||
String[] localeParts = localeAsString.split("_");
|
||||
|
||||
if (localeParts != null && localeParts.length == 1) {
|
||||
languageCode = localeParts[0];
|
||||
}
|
||||
|
||||
if (localeParts != null && localeParts.length == 2) {
|
||||
languageCode = localeParts[0];
|
||||
courntryCode = localeParts[1];
|
||||
}
|
||||
|
||||
if (localeParts != null && localeParts.length == 3) {
|
||||
languageCode = localeParts[0];
|
||||
courntryCode = localeParts[1];
|
||||
variantCode = localeParts[2];
|
||||
}
|
||||
|
||||
return localeFrom(languageCode, courntryCode, variantCode);
|
||||
}
|
||||
|
||||
private Locale localeFrom(final String languageCode, final String courntryCode, final String variantCode) {
|
||||
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
List<String> allowedLanguages = Arrays.asList(Locale.getISOLanguages());
|
||||
if (!allowedLanguages.contains(languageCode.toLowerCase())) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", "The parameter locale has an invalid language value " + languageCode + " .", "locale", languageCode);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(courntryCode.toUpperCase())) {
|
||||
List<String> allowedCountries = Arrays.asList(Locale.getISOCountries());
|
||||
if (!allowedCountries.contains(courntryCode)) {
|
||||
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.locale.format", "The parameter locale has an invalid country value " + courntryCode + " .", "locale", courntryCode);
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
return new Locale(languageCode.toLowerCase(), courntryCode.toUpperCase(), variantCode);
|
||||
}
|
||||
|
||||
private Locale extractLocaleValue(final JsonObject object) {
|
||||
Locale clientApplicationLocale = null;
|
||||
String locale = null;
|
||||
if (object.has("locale") && object.get("locale").isJsonPrimitive()) {
|
||||
JsonPrimitive primitive = object.get("locale").getAsJsonPrimitive();
|
||||
locale = primitive.getAsString();
|
||||
clientApplicationLocale = localeFromString(locale);
|
||||
}
|
||||
return clientApplicationLocale;
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,9 @@ import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.commands.LoanApplicationCommand;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
@@ -244,7 +243,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
public void modifyLoanApplication(final LoanApplicationCommand command, final Client client, final LoanProduct loanProduct,
|
||||
final Fund fund, final LoanTransactionProcessingStrategy strategy, final LoanSchedule modifiedLoanSchedule, final Set<LoanCharge> charges,
|
||||
final Fund fund, final LoanTransactionProcessingStrategy strategy, final NewLoanScheduleData modifiedLoanSchedule, final Set<LoanCharge> charges,
|
||||
final Staff loanOfficer) {
|
||||
|
||||
if (command.isClientChanged()) {
|
||||
@@ -310,16 +309,16 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
// FIXME - rewrite over loan schedule by default for now but worth putting in check to see if required
|
||||
// i.e. only a client change wouldn't require it, only if one of parameters related to loan schedule calculation is changed.
|
||||
this.repaymentScheduleInstallments.clear();
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : modifiedLoanSchedule.getScheduledLoanInstallments()) {
|
||||
|
||||
MoneyData readPrincipalDue = scheduledLoanInstallment.getPrincipalDue();
|
||||
MoneyData readInterestDue = scheduledLoanInstallment.getInterestDue();
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
this, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
|
||||
readInterestDue.getAmount());
|
||||
this.addRepaymentScheduleInstallment(installment);
|
||||
for (LoanSchedulePeriodData scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
this, scheduledLoanInstallment.periodNumber(),
|
||||
scheduledLoanInstallment.periodDueDate(),
|
||||
scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue());
|
||||
this.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
|
||||
// if the loan application/contract is modified when repayments are already against it - then need to re-process it
|
||||
|
||||
-3
@@ -3,12 +3,9 @@ package org.mifosng.platform.loan.service;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
|
||||
public interface CalculationPlatformService {
|
||||
|
||||
LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command);
|
||||
|
||||
NewLoanScheduleData calculateLoanScheduleNew(CalculateLoanScheduleCommand command);
|
||||
|
||||
/**
|
||||
|
||||
+7
-80
@@ -1,30 +1,20 @@
|
||||
package org.mifosng.platform.loan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrencyRepository;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.exceptions.CurrencyNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosng.platform.loan.domain.InterestMethod;
|
||||
import org.mifosng.platform.loan.domain.Loan;
|
||||
import org.mifosng.platform.loan.domain.LoanPayoffSummary;
|
||||
import org.mifosng.platform.loan.domain.LoanProduct;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanRepository;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.loanschedule.domain.AprCalculator;
|
||||
import org.mifosng.platform.loanschedule.domain.DefaultLoanScheduleGeneratorFactory;
|
||||
import org.mifosng.platform.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.mifosng.platform.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
@@ -37,9 +27,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
|
||||
|
||||
private final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final ApplicationCurrencyRepository applicationCurrencyRepository;
|
||||
private final AprCalculator aprCalculator;
|
||||
private final PlatformSecurityContext context;
|
||||
private final LoanProductRelatedDetailAssembler loanProductRelatedDetailAssembler;
|
||||
|
||||
@@ -47,15 +35,11 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
|
||||
public CalculationPlatformServiceImpl(
|
||||
final PlatformSecurityContext context,
|
||||
final LoanRepository loanRepository,
|
||||
final LoanProductRepository loanProductRepository,
|
||||
final ApplicationCurrencyRepository applicationCurrencyRepository,
|
||||
final AprCalculator aprCalculator,
|
||||
final LoanProductRelatedDetailAssembler loanProductRelatedDetailAssembler) {
|
||||
this.context = context;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.aprCalculator = aprCalculator;
|
||||
this.loanScheduleFactory = new DefaultLoanScheduleGeneratorFactory();
|
||||
this.loanProductRelatedDetailAssembler = loanProductRelatedDetailAssembler;
|
||||
}
|
||||
@@ -70,76 +54,19 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
|
||||
|
||||
LoanProductRelatedDetail loanScheduleRelatedDetails = this.loanProductRelatedDetailAssembler.assembleFrom(command);
|
||||
|
||||
// final Integer loanTermFrequency = command.getLoanTermFrequency();
|
||||
// final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
|
||||
|
||||
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
|
||||
|
||||
// ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode());
|
||||
|
||||
// CurrencyData currencyData = new CurrencyData(
|
||||
// applicationCurrency.getCode(),
|
||||
// applicationCurrency.getName(),
|
||||
// loanScheduleRelatedDetails.getCurrency().getDigitsAfterDecimal(),
|
||||
// applicationCurrency.getDisplaySymbol(),
|
||||
// applicationCurrency.getNameCode());
|
||||
|
||||
return loanScheduleGenerator.generate(loanScheduleRelatedDetails,
|
||||
command.getExpectedDisbursementDate(),
|
||||
command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestChargedFromDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanSchedule calculateLoanSchedule(final CalculateLoanScheduleCommand command) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
final BigDecimal principalAmount = command.getPrincipal();
|
||||
final BigDecimal defaultNominalInterestRatePerPeriod = command.getInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyType());
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodType());
|
||||
|
||||
final Integer loanTermFrequency = command.getLoanTermFrequency();
|
||||
final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
|
||||
|
||||
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
|
||||
final Integer repayEvery = command.getRepaymentEvery();
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequencyType());
|
||||
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationType());
|
||||
|
||||
final BigDecimal defaultAnnualNominalInterestRate = this.aprCalculator.calculateFrom(interestPeriodFrequencyType, defaultNominalInterestRatePerPeriod);
|
||||
|
||||
LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
|
||||
if (loanProduct == null) {
|
||||
throw new LoanProductNotFoundException(command.getProductId());
|
||||
}
|
||||
|
||||
final MonetaryCurrency currency = loanProduct.getCurrency();
|
||||
|
||||
// in arrerars tolerance isnt relevant when auto-calculating a loan schedule
|
||||
final BigDecimal inArrearsTolerance = BigDecimal.ZERO;
|
||||
|
||||
LoanProductRelatedDetail loanScheduleInfo = new LoanProductRelatedDetail(currency, principalAmount,
|
||||
defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
|
||||
interestMethod, interestCalculationPeriodMethod,
|
||||
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod,
|
||||
inArrearsTolerance);
|
||||
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestType());
|
||||
|
||||
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(interestMethod);
|
||||
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(currency.getCode());
|
||||
CurrencyData currencyData = new CurrencyData(applicationCurrency.getCode(), applicationCurrency.getName(), currency.getDigitsAfterDecimal(),
|
||||
applicationCurrency.getDisplaySymbol(), applicationCurrency.getNameCode());
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneByCode(loanScheduleRelatedDetails.getCurrency().getCode());
|
||||
|
||||
return loanScheduleGenerator.generate(loanScheduleInfo, loanTermFrequency, loanTermFrequencyType, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestChargedFromDate(), currencyData);
|
||||
return loanScheduleGenerator.generate(applicationCurrency,
|
||||
loanScheduleRelatedDetails, loanTermFrequency,
|
||||
loanTermFrequencyType, command.getExpectedDisbursementDate(),
|
||||
command.getRepaymentsStartingFromDate(),
|
||||
command.getInterestChargedFromDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-18
@@ -1,16 +1,15 @@
|
||||
package org.mifosng.platform.loan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.commands.LoanApplicationCommand;
|
||||
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.charge.domain.Charge;
|
||||
import org.mifosng.platform.charge.domain.ChargeRepository;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
@@ -59,6 +58,7 @@ public class LoanAssembler {
|
||||
private final ChargeRepository chargeRepository;
|
||||
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
|
||||
private final StaffRepository staffRepository;
|
||||
private final CalculationPlatformService calculationPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanAssembler(
|
||||
@@ -67,13 +67,15 @@ public class LoanAssembler {
|
||||
final FundRepository fundRepository,
|
||||
final ChargeRepository chargeRepository,
|
||||
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
|
||||
final StaffRepository staffRepository) {
|
||||
final StaffRepository staffRepository,
|
||||
final CalculationPlatformService calculationPlatformService) {
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.clientRepository = clientRepository;
|
||||
this.fundRepository = fundRepository;
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
|
||||
this.staffRepository = staffRepository;
|
||||
this.calculationPlatformService = calculationPlatformService;
|
||||
}
|
||||
|
||||
public Loan assembleFrom(final LoanApplicationCommand command) {
|
||||
@@ -107,21 +109,17 @@ public class LoanAssembler {
|
||||
Loan loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
// TODO - user service to calculate loan schedule at this point
|
||||
// LoanSchedule loanSchedule = command.getLoanSchedule();
|
||||
List<ScheduledLoanInstallment> loanRepaymentSchedule = new ArrayList<ScheduledLoanInstallment>();
|
||||
// loanSchedule.getScheduledLoanInstallments();
|
||||
final NewLoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||
|
||||
MoneyData readPrincipalDue = scheduledLoanInstallment.getPrincipalDue();
|
||||
MoneyData readInterestDue = scheduledLoanInstallment.getInterestDue();
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
|
||||
readInterestDue.getAmount());
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
for (LoanSchedulePeriodData scheduledLoanInstallment : loanSchedule.getPeriods()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.periodNumber(),
|
||||
scheduledLoanInstallment.periodDueDate(),
|
||||
scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue());
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
|
||||
loan.submitApplication(loanTermFrequency, loanTermFrequencyType,
|
||||
|
||||
+16
-26
@@ -8,6 +8,7 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.commands.AdjustLoanTransactionCommand;
|
||||
import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
|
||||
import org.mifosng.platform.api.commands.LoanApplicationCommand;
|
||||
@@ -15,13 +16,11 @@ import org.mifosng.platform.api.commands.LoanStateTransitionCommand;
|
||||
import org.mifosng.platform.api.commands.LoanTransactionCommand;
|
||||
import org.mifosng.platform.api.commands.UndoStateTransitionCommand;
|
||||
import org.mifosng.platform.api.data.EntityIdentifier;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.client.domain.Client;
|
||||
import org.mifosng.platform.client.domain.ClientRepository;
|
||||
import org.mifosng.platform.client.domain.Note;
|
||||
import org.mifosng.platform.client.domain.NoteRepository;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanNotFoundException;
|
||||
@@ -147,7 +146,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
||||
Set<LoanCharge> charges = this.loanAssembler.assembleSetOfLoanCharges(command.getCharges(), loanProduct.getCharges(), loan.getCurrency().getCode());
|
||||
|
||||
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
|
||||
final NewLoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
|
||||
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer);
|
||||
|
||||
this.loanRepository.save(loan);
|
||||
@@ -332,7 +331,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
Integer selectedRepaymentFrequency = loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue();
|
||||
Integer selectedAmortizationMethod = loan.repaymentScheduleDetail().getAmortizationMethod().getValue();
|
||||
|
||||
// FIXME - use values from loan table here instead of inferring.
|
||||
// FIXME - KW - use values from loan table here instead of inferring.
|
||||
Integer loanTermFrequency = repaidEvery * numberOfInstallments;
|
||||
Integer loanTermFrequencyType = selectedRepaymentFrequency;
|
||||
|
||||
@@ -351,29 +350,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanTermFrequency, loanTermFrequencyType,
|
||||
actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
|
||||
|
||||
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand);
|
||||
NewLoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(calculateCommand);
|
||||
|
||||
List<LoanRepaymentScheduleInstallment> modifiedLoanRepaymentSchedule = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanSchedule
|
||||
.getScheduledLoanInstallments()) {
|
||||
|
||||
final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(
|
||||
scheduledLoanInstallment.getPrincipalDue().getCurrencyCode(),
|
||||
scheduledLoanInstallment.getPrincipalDue().getDigitsAfterDecimal());
|
||||
|
||||
Money principal = Money.of(monetaryCurrency,
|
||||
scheduledLoanInstallment.getPrincipalDue().getAmount());
|
||||
|
||||
Money interest = Money.of(monetaryCurrency,
|
||||
scheduledLoanInstallment.getInterestDue().getAmount());
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.getInstallmentNumber(),
|
||||
// scheduledLoanInstallment.getPeriodStart(),
|
||||
scheduledLoanInstallment.getPeriodEnd(), principal.getAmount(),
|
||||
interest.getAmount());
|
||||
modifiedLoanRepaymentSchedule.add(installment);
|
||||
for (LoanSchedulePeriodData scheduledLoanPeriod : loanSchedule.getPeriods()) {
|
||||
if (scheduledLoanPeriod.isRepaymentPeriod()) {
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanPeriod.periodNumber(),
|
||||
scheduledLoanPeriod.periodDueDate(),
|
||||
scheduledLoanPeriod.principalDue(),
|
||||
scheduledLoanPeriod.interestDue());
|
||||
|
||||
modifiedLoanRepaymentSchedule.add(installment);
|
||||
}
|
||||
}
|
||||
loan.disburseWithModifiedRepaymentSchedule(actualDisbursementDate, modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine());
|
||||
} else {
|
||||
@@ -402,7 +392,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected()) {
|
||||
// FIXME - handle this use case - recalculate loan schedule using original settings.
|
||||
// FIXME - KW - handle this use case - recalculate loan schedule using original settings.
|
||||
}
|
||||
|
||||
loan.undoDisbursal(defaultLoanLifecycleStateMachine());
|
||||
|
||||
+12
-7
@@ -7,17 +7,23 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
|
||||
public interface AmortizationLoanScheduleGenerator {
|
||||
|
||||
LoanSchedule generate(LoanProductRelatedDetail loanScheduleInfo, LocalDate disbursementDate,
|
||||
LocalDate firstRepaymentDate, LocalDate interestCalculatedFrom, CurrencyData currencyData,
|
||||
BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
List<LocalDate> scheduledDates);
|
||||
@Deprecated
|
||||
LoanSchedule generate(final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
final LocalDate interestCalculatedFrom,
|
||||
final CurrencyData currencyData,
|
||||
final BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
final List<LocalDate> scheduledDates);
|
||||
|
||||
NewLoanScheduleData generate(
|
||||
ApplicationCurrency applicationCurrency,
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate,
|
||||
LocalDate firstRepaymentDate,
|
||||
@@ -25,5 +31,4 @@ public interface AmortizationLoanScheduleGenerator {
|
||||
BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
List<LocalDate> scheduledDates);
|
||||
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -7,6 +7,7 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
@@ -36,7 +37,10 @@ public class DecliningBalanceMethodLoanScheduleGenerator implements LoanSchedule
|
||||
|
||||
@Override
|
||||
public NewLoanScheduleData generate(
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final ApplicationCurrency applicationCurrency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermFrequencyType,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
final LocalDate interestCalculatedFrom) {
|
||||
@@ -52,7 +56,7 @@ public class DecliningBalanceMethodLoanScheduleGenerator implements LoanSchedule
|
||||
// Determine with 'amortisation' approach to use
|
||||
final AmortizationLoanScheduleGenerator generator = this.amortizationLoanScheduleGeneratorFactory.createGenerator(loanScheduleInfo.getAmortizationMethod());
|
||||
|
||||
return generator.generate(
|
||||
return generator.generate(applicationCurrency,
|
||||
loanScheduleInfo,
|
||||
disbursementDate,
|
||||
firstRepaymentDate,
|
||||
@@ -62,6 +66,7 @@ public class DecliningBalanceMethodLoanScheduleGenerator implements LoanSchedule
|
||||
scheduledDates);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public LoanSchedule generate(
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
|
||||
+28
-7
@@ -13,6 +13,7 @@ import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
@@ -28,6 +29,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
|
||||
@Override
|
||||
public NewLoanScheduleData generate(
|
||||
final ApplicationCurrency currency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
@@ -36,10 +38,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
final List<LocalDate> scheduledDates) {
|
||||
|
||||
// 1. add disbursements to loan schedule
|
||||
|
||||
// 2.
|
||||
Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
// determine 'total payment' for each repayment based on pmt function (and hence the total due overall)
|
||||
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
|
||||
@@ -58,11 +57,17 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
scheduledDates,
|
||||
idealDisbursementDateBasedOnFirstRepaymentDate);
|
||||
|
||||
|
||||
// create entries of disbursement period on loan schedule
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursement(disbursementDate, loanScheduleInfo.getPrincipal().getAmount());
|
||||
periods.add(disbursementPeriod);
|
||||
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
|
||||
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeChargesToDate = BigDecimal.ZERO;
|
||||
BigDecimal totalExpectedRepayment = BigDecimal.ZERO;
|
||||
|
||||
LocalDate startDate = disbursementDate;
|
||||
int periodNumber = 1;
|
||||
for (LocalDate scheduledDueDate : scheduledDates) {
|
||||
@@ -115,15 +120,31 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
interestForInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += daysInPeriod;
|
||||
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
|
||||
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
|
||||
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
|
||||
startDate = scheduledDueDate;
|
||||
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
return new NewLoanScheduleData(periods);
|
||||
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(
|
||||
currency.getCode(),
|
||||
currency.getName(),
|
||||
monetaryCurrency.getDigitsAfterDecimal(),
|
||||
currency.getDisplaySymbol(),
|
||||
currency.getNameCode());
|
||||
|
||||
return new NewLoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public LoanSchedule generate(final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
|
||||
+95
-2
@@ -2,6 +2,7 @@ package org.mifosng.platform.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.Days;
|
||||
@@ -9,8 +10,10 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
@@ -22,16 +25,106 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
|
||||
|
||||
@Override
|
||||
public NewLoanScheduleData generate(
|
||||
ApplicationCurrency currency,
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate, LocalDate firstRepaymentDate,
|
||||
LocalDate interestCalculatedFrom,
|
||||
BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
List<LocalDate> scheduledDates) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
// 1. we know 'principal' must be equal over repayments so use this to calculate interest and thus total repayment due.
|
||||
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
|
||||
Money totalDuePerInstallment = Money.zero(monetaryCurrency);
|
||||
|
||||
Money outstandingBalance = loanScheduleInfo.getPrincipal();
|
||||
Money totalPrincipal = Money.zero(monetaryCurrency);
|
||||
Money totalInterest = Money.zero(monetaryCurrency);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator.calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(),
|
||||
loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates, idealDisbursementDateBasedOnFirstRepaymentDate);
|
||||
|
||||
// create entries of disbursement period on loan schedule
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursement(disbursementDate, loanScheduleInfo.getPrincipal().getAmount());
|
||||
periods.add(disbursementPeriod);
|
||||
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
|
||||
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeChargesToDate = BigDecimal.ZERO;
|
||||
BigDecimal totalExpectedRepayment = 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());
|
||||
}
|
||||
}
|
||||
|
||||
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment);
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalForInstallment);
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalForInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestForInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += daysInPeriod;
|
||||
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
|
||||
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
|
||||
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
|
||||
startDate = scheduledDueDate;
|
||||
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(
|
||||
currency.getCode(),
|
||||
currency.getName(),
|
||||
monetaryCurrency.getDigitsAfterDecimal(),
|
||||
currency.getDisplaySymbol(),
|
||||
currency.getNameCode());
|
||||
|
||||
return new NewLoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public LoanSchedule generate(final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
|
||||
+107
-4
@@ -4,14 +4,19 @@ 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 org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.ScheduledLoanInstallment;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
@@ -23,10 +28,108 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
@Override
|
||||
public NewLoanScheduleData generate(
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate, LocalDate firstRepaymentDate,
|
||||
LocalDate interestCalculatedFrom) {
|
||||
return null;
|
||||
final ApplicationCurrency currency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermFrequencyType,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
final LocalDate interestCalculatedFrom) {
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
final List<LocalDate> 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 totalPrincipal = Money.zero(outstandingBalance.getCurrency());
|
||||
Money totalInterest = Money.zero(outstandingBalance.getCurrency());
|
||||
|
||||
// create entries of disbursement period on loan schedule
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursement(disbursementDate, loanScheduleInfo.getPrincipal().getAmount());
|
||||
periods.add(disbursementPeriod);
|
||||
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
|
||||
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeChargesToDate = BigDecimal.ZERO;
|
||||
BigDecimal totalExpectedRepayment = 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());
|
||||
}
|
||||
}
|
||||
|
||||
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment);
|
||||
outstandingBalance = outstandingBalance.minus(principalPerInstallment);
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalPerInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestPerInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += daysInPeriod;
|
||||
cumulativePrincipalDue = cumulativePrincipalDue.add(principalPerInstallment.getAmount());
|
||||
cumulativeInterestExpected = cumulativeInterestExpected.add(interestPerInstallment.getAmount());
|
||||
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
|
||||
startDate = scheduledDueDate;
|
||||
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(
|
||||
currency.getCode(),
|
||||
currency.getName(),
|
||||
monetaryCurrency.getDigitsAfterDecimal(),
|
||||
currency.getDisplaySymbol(),
|
||||
currency.getNameCode());
|
||||
|
||||
return new NewLoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -4,17 +4,23 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.NewLoanScheduleData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.LoanSchedule;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
|
||||
public interface LoanScheduleGenerator {
|
||||
|
||||
@Deprecated
|
||||
LoanSchedule generate(LoanProductRelatedDetail loanScheduleInfo,
|
||||
Integer loanTermFrequency,
|
||||
PeriodFrequencyType loanTermFrequencyType,
|
||||
LocalDate disbursementDate, LocalDate firstRepaymentDate, LocalDate interestCalculatedFrom,
|
||||
CurrencyData currencyData);
|
||||
|
||||
NewLoanScheduleData generate(LoanProductRelatedDetail loanScheduleInfo, LocalDate disbursementDate, LocalDate firstRepaymentDate, LocalDate interestCalculatedFrom);
|
||||
NewLoanScheduleData generate(ApplicationCurrency applicationCurrency,
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
Integer loanTermFrequency,
|
||||
PeriodFrequencyType loanTermFrequencyType,
|
||||
LocalDate disbursementDate, LocalDate firstRepaymentDate, LocalDate interestCalculatedFrom);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user