Inital support for adding loan charge due on specified date for demo to gk.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
alter table m_loan_charge
|
||||
add column due_for_collection_as_of_date date DEFAULT NULL after charge_time_enum;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column fee_charges_amount decimal(19,6) DEFAULT NULL after interest_writtenoff_derived;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column fee_charges_completed_derived decimal(19,6) DEFAULT NULL after fee_charges_amount;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column fee_charges_writtenoff_derived decimal(19,6) DEFAULT NULL after fee_charges_completed_derived;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column fee_charges_waived_derived decimal(19,6) DEFAULT NULL after fee_charges_writtenoff_derived;
|
||||
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column penalty_charges_amount decimal(19,6) DEFAULT NULL after fee_charges_waived_derived;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column penalty_charges_completed_derived decimal(19,6) DEFAULT NULL after penalty_charges_amount;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column penalty_charges_writtenoff_derived decimal(19,6) DEFAULT NULL after penalty_charges_completed_derived;
|
||||
|
||||
alter table m_loan_repayment_schedule
|
||||
add column penalty_charges_waived_derived decimal(19,6) DEFAULT NULL after penalty_charges_writtenoff_derived;
|
||||
|
||||
/**
|
||||
modify existing columns
|
||||
*/
|
||||
alter table `m_loan_repayment_schedule` MODIFY `duedate` date NOT NULL;
|
||||
alter table `m_loan_repayment_schedule` MODIFY `installment` smallint(5) NOT NULL;
|
||||
alter table `m_loan_repayment_schedule` MODIFY `interest_waived_derived` decimal(19,6) DEFAULT NULL;
|
||||
@@ -311,7 +311,7 @@ public class LoansApiResource {
|
||||
@Context final UriInfo uriInfo,
|
||||
final String jsonRequestBody) {
|
||||
|
||||
LoanApplicationCommand command = this.apiDataConversionService.convertJsonToLoanApplicationCommand(null, jsonRequestBody);
|
||||
final LoanApplicationCommand command = this.apiDataConversionService.convertJsonToLoanApplicationCommand(null, jsonRequestBody);
|
||||
|
||||
if (is(commandParam, "calculateLoanSchedule")) {
|
||||
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
|
||||
@@ -325,7 +325,7 @@ public class LoansApiResource {
|
||||
|
||||
private String calculateLoanSchedule(final UriInfo uriInfo, final CalculateLoanScheduleCommand command) {
|
||||
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command);
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command);
|
||||
|
||||
final Set<String> typicalLoanScheduleResponseParameters = new HashSet<String>(
|
||||
Arrays.asList("periods", "cumulativePrincipalDisbursed"));
|
||||
|
||||
+35
-8
@@ -3,6 +3,11 @@ package org.mifosng.platform.api.commands;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable command for creating and updating loan charges.
|
||||
*/
|
||||
public class LoanChargeCommand {
|
||||
|
||||
private final Long id;
|
||||
@@ -11,18 +16,32 @@ public class LoanChargeCommand {
|
||||
private final BigDecimal amount;
|
||||
|
||||
private final Integer chargeTimeType;
|
||||
private final LocalDate specifiedDueDate;
|
||||
private final Integer chargeCalculationType;
|
||||
|
||||
private final Set<String> modifiedParameters;
|
||||
/**
|
||||
* Used to capture what parameters were passed in the json api request.
|
||||
* It does not indicate that these values are modified from their original values when tyring to update.
|
||||
*/
|
||||
private final Set<String> parametersPassedInCommand;
|
||||
|
||||
public LoanChargeCommand(Set<String> modifiedParameters, Long id, Long loanId, Long chargeId, BigDecimal amount, Integer chargeTimeType, Integer chargeCalculationType) {
|
||||
this.modifiedParameters = modifiedParameters;
|
||||
public LoanChargeCommand(
|
||||
final Set<String> parametersPassedInCommand,
|
||||
final Long id,
|
||||
final Long loanId,
|
||||
final Long chargeId,
|
||||
final BigDecimal amount,
|
||||
final Integer chargeTimeType,
|
||||
final Integer chargeCalculationType,
|
||||
final LocalDate specifiedDueDate) {
|
||||
this.parametersPassedInCommand = parametersPassedInCommand;
|
||||
this.id = id;
|
||||
this.chargeId = chargeId;
|
||||
this.loanId = loanId;
|
||||
this.amount = amount;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.specifiedDueDate = specifiedDueDate;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@@ -44,20 +63,28 @@ public class LoanChargeCommand {
|
||||
public Integer getChargeTimeType() {
|
||||
return chargeTimeType;
|
||||
}
|
||||
|
||||
public LocalDate getSpecifiedDueDate() {
|
||||
return specifiedDueDate;
|
||||
}
|
||||
|
||||
public Integer getChargeCalculationType() {
|
||||
public Integer getChargeCalculationType() {
|
||||
return chargeCalculationType;
|
||||
}
|
||||
|
||||
public boolean isAmountChanged(){
|
||||
return this.modifiedParameters.contains("amount");
|
||||
return this.parametersPassedInCommand.contains("amount");
|
||||
}
|
||||
|
||||
public boolean isChargeTimeTypeChanged(){
|
||||
return this.modifiedParameters.contains("chargeTimeType");
|
||||
return this.parametersPassedInCommand.contains("chargeTimeType");
|
||||
}
|
||||
|
||||
public boolean isSpecifiedDueDateChanged(){
|
||||
return this.parametersPassedInCommand.contains("specifiedDueDate");
|
||||
}
|
||||
|
||||
public boolean isChargeCalculationTypeChanged(){
|
||||
return this.modifiedParameters.contains("chargeCalculationType");
|
||||
return this.parametersPassedInCommand.contains("chargeCalculationType");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.mifosng.platform.api.data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable data object for loan charge data.
|
||||
*/
|
||||
@@ -10,25 +12,39 @@ public class LoanChargeData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long chargeId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeTimeType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate dueAsOfDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData chargeCalculationType;
|
||||
|
||||
private final BigDecimal percentage;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountPercentageAppliedTo;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final CurrencyData currency;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amount;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountPaid;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal amountOrPercentage;
|
||||
|
||||
@@ -61,6 +77,7 @@ public class LoanChargeData {
|
||||
final BigDecimal amountPaid,
|
||||
final BigDecimal amountOutstanding,
|
||||
final EnumOptionData chargeTimeType,
|
||||
final LocalDate dueAsOfDate,
|
||||
final EnumOptionData chargeCalculationType,
|
||||
final BigDecimal percentage,
|
||||
final BigDecimal amountPercentageAppliedTo) {
|
||||
@@ -72,6 +89,7 @@ public class LoanChargeData {
|
||||
this.amountPaid = amountPaid;
|
||||
this.amountOutstanding = amountOutstanding;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.dueAsOfDate = dueAsOfDate;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.percentage = percentage;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
@@ -102,6 +120,7 @@ public class LoanChargeData {
|
||||
this.amountPaid = BigDecimal.ZERO;
|
||||
this.amountOutstanding = amount;
|
||||
this.chargeTimeType = chargeTimeType;
|
||||
this.dueAsOfDate = null;
|
||||
this.chargeCalculationType = chargeCalculationType;
|
||||
this.percentage = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
|
||||
+53
-23
@@ -33,9 +33,13 @@ public class LoanSchedulePeriodData {
|
||||
private final BigDecimal interestWrittenOff;
|
||||
private final BigDecimal interestOutstanding;
|
||||
|
||||
private final BigDecimal chargesDue;
|
||||
private final BigDecimal chargesPaid;
|
||||
private final BigDecimal chargesOutstanding;
|
||||
private final BigDecimal feeChargesDue;
|
||||
private final BigDecimal feeChargesPaid;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal feeChargesWaived;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal feeChargesWrittenOff;
|
||||
private final BigDecimal feeChargesOutstanding;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal totalOriginalDueForPeriod;
|
||||
@@ -60,11 +64,17 @@ public class LoanSchedulePeriodData {
|
||||
return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, chargesDueAtTimeOfDisbursement, isDisbursed);
|
||||
}
|
||||
|
||||
public static LoanSchedulePeriodData repaymentOnlyPeriod(final Integer periodNumber,
|
||||
final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principalDue,
|
||||
final BigDecimal principalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal totalDueForPeriod) {
|
||||
public static LoanSchedulePeriodData repaymentOnlyPeriod(
|
||||
final Integer periodNumber,
|
||||
final LocalDate fromDate,
|
||||
final LocalDate dueDate,
|
||||
final BigDecimal principalDue,
|
||||
final BigDecimal principalOutstanding,
|
||||
final BigDecimal interestDueOnPrincipalOutstanding,
|
||||
final BigDecimal chargesDueForPeriod,
|
||||
final BigDecimal totalDueForPeriod) {
|
||||
|
||||
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding, interestDueOnPrincipalOutstanding, totalDueForPeriod);
|
||||
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding, interestDueOnPrincipalOutstanding, chargesDueForPeriod, totalDueForPeriod);
|
||||
}
|
||||
|
||||
public static LoanSchedulePeriodData repaymentPeriodWithPayments(
|
||||
@@ -82,6 +92,11 @@ public class LoanSchedulePeriodData {
|
||||
final BigDecimal interestWaived,
|
||||
final BigDecimal interestWrittenOff,
|
||||
final BigDecimal interestOutstanding,
|
||||
final BigDecimal feeChargesDue,
|
||||
final BigDecimal feeChargesPaid,
|
||||
final BigDecimal feeChargesWaived,
|
||||
final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesOutstanding,
|
||||
final BigDecimal totalDueForPeriod,
|
||||
final BigDecimal totalPaid,
|
||||
final BigDecimal totalWaived,
|
||||
@@ -91,6 +106,7 @@ public class LoanSchedulePeriodData {
|
||||
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
|
||||
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
||||
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
|
||||
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
|
||||
totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding);
|
||||
}
|
||||
|
||||
@@ -127,21 +143,25 @@ public class LoanSchedulePeriodData {
|
||||
this.interestWrittenOff = null;
|
||||
this.interestOutstanding = null;
|
||||
|
||||
this.chargesDue = chargesDueAtTimeOfDisbursement;
|
||||
this.feeChargesDue = chargesDueAtTimeOfDisbursement;
|
||||
if (isDisbursed) {
|
||||
this.chargesPaid = chargesDueAtTimeOfDisbursement;
|
||||
this.chargesOutstanding = BigDecimal.ZERO;
|
||||
this.feeChargesPaid = chargesDueAtTimeOfDisbursement;
|
||||
this.feeChargesWaived = BigDecimal.ZERO;
|
||||
this.feeChargesWrittenOff = BigDecimal.ZERO;
|
||||
this.feeChargesOutstanding = BigDecimal.ZERO;
|
||||
} else {
|
||||
this.chargesPaid = BigDecimal.ZERO;
|
||||
this.chargesOutstanding = chargesDueAtTimeOfDisbursement;
|
||||
this.feeChargesPaid = BigDecimal.ZERO;
|
||||
this.feeChargesWaived = BigDecimal.ZERO;
|
||||
this.feeChargesWrittenOff = BigDecimal.ZERO;
|
||||
this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement;
|
||||
}
|
||||
|
||||
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
|
||||
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
|
||||
this.totalPaidForPeriod = this.chargesPaid;
|
||||
this.totalPaidForPeriod = this.feeChargesPaid;
|
||||
this.totalWaivedForPeriod = null;
|
||||
this.totalWrittenOffForPeriod = null;
|
||||
this.totalOutstandingForPeriod = this.chargesOutstanding;
|
||||
this.totalOutstandingForPeriod = this.feeChargesOutstanding;
|
||||
if (dueDate.isBefore(new LocalDate())) {
|
||||
this.totalOverdue = this.totalOutstandingForPeriod;
|
||||
} else {
|
||||
@@ -156,6 +176,7 @@ public class LoanSchedulePeriodData {
|
||||
final BigDecimal principalOriginalDue,
|
||||
final BigDecimal principalOutstanding,
|
||||
final BigDecimal interestDueOnPrincipalOutstanding,
|
||||
final BigDecimal chargesDueForPeriod,
|
||||
final BigDecimal totalDueForPeriod) {
|
||||
this.period = periodNumber;
|
||||
this.fromDate = fromDate;
|
||||
@@ -180,9 +201,11 @@ public class LoanSchedulePeriodData {
|
||||
this.interestWrittenOff = null;
|
||||
this.interestOutstanding = interestDueOnPrincipalOutstanding;
|
||||
|
||||
this.chargesDue = BigDecimal.ZERO;
|
||||
this.chargesPaid = null;
|
||||
this.chargesOutstanding = null;
|
||||
this.feeChargesDue = chargesDueForPeriod;
|
||||
this.feeChargesPaid = null;
|
||||
this.feeChargesWaived = null;
|
||||
this.feeChargesWrittenOff = null;
|
||||
this.feeChargesOutstanding = null;
|
||||
|
||||
this.totalOriginalDueForPeriod = totalDueForPeriod;
|
||||
this.totalDueForPeriod = totalDueForPeriod;
|
||||
@@ -214,6 +237,11 @@ public class LoanSchedulePeriodData {
|
||||
final BigDecimal interestWaived,
|
||||
final BigDecimal interestWrittenOff,
|
||||
final BigDecimal interestOutstanding,
|
||||
final BigDecimal feeChargesDue,
|
||||
final BigDecimal feeChargesPaid,
|
||||
final BigDecimal feeChargesWaived,
|
||||
final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesOutstanding,
|
||||
final BigDecimal totalDueForPeriod,
|
||||
final BigDecimal totalPaid,
|
||||
final BigDecimal totalWaived,
|
||||
@@ -242,9 +270,11 @@ public class LoanSchedulePeriodData {
|
||||
this.interestWrittenOff = interestWrittenOff;
|
||||
this.interestOutstanding = interestOutstanding;
|
||||
|
||||
this.chargesDue = BigDecimal.ZERO;
|
||||
this.chargesPaid = BigDecimal.ZERO;
|
||||
this.chargesOutstanding = BigDecimal.ZERO;
|
||||
this.feeChargesDue = feeChargesDue;
|
||||
this.feeChargesPaid = feeChargesPaid;
|
||||
this.feeChargesWaived = feeChargesWaived;
|
||||
this.feeChargesWrittenOff = feeChargesWrittenOff;
|
||||
this.feeChargesOutstanding = feeChargesOutstanding;
|
||||
|
||||
this.totalOriginalDueForPeriod = totalDueForPeriod;
|
||||
this.totalDueForPeriod = totalDueForPeriod;
|
||||
@@ -329,15 +359,15 @@ public class LoanSchedulePeriodData {
|
||||
}
|
||||
|
||||
public BigDecimal chargesDue() {
|
||||
return defaultToZeroIfNull(this.chargesDue);
|
||||
return defaultToZeroIfNull(this.feeChargesDue);
|
||||
}
|
||||
|
||||
public BigDecimal chargesPaid() {
|
||||
return defaultToZeroIfNull(this.chargesPaid);
|
||||
return defaultToZeroIfNull(this.feeChargesPaid);
|
||||
}
|
||||
|
||||
public BigDecimal chargesOutstanding() {
|
||||
return defaultToZeroIfNull(this.chargesOutstanding);
|
||||
return defaultToZeroIfNull(this.feeChargesOutstanding);
|
||||
}
|
||||
|
||||
public BigDecimal totalOverdue() {
|
||||
|
||||
+50
-47
@@ -465,67 +465,68 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
|
||||
final Set<String> modifiedParameters = new HashSet<String>();
|
||||
final Set<String> parametersPassedInCommand = new HashSet<String>();
|
||||
|
||||
final JsonParser parser = new JsonParser();
|
||||
final JsonElement element = parser.parse(json);
|
||||
final JsonParserHelper helper = new JsonParserHelper();
|
||||
|
||||
final Long clientId = helper.extractLongNamed("clientId", element, modifiedParameters);
|
||||
final Long groupId = helper.extractLongNamed("groupId", 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);
|
||||
final Long clientId = helper.extractLongNamed("clientId", element, parametersPassedInCommand);
|
||||
final Long groupId = helper.extractLongNamed("groupId", element, parametersPassedInCommand);
|
||||
final Long productId = helper.extractLongNamed("productId", element, parametersPassedInCommand);
|
||||
final Long fundId = helper.extractLongNamed("fundId", element, parametersPassedInCommand);
|
||||
final Long loanOfficerId = helper.extractLongNamed("loanOfficerId", element, parametersPassedInCommand);
|
||||
final Long transactionProcessingStrategyId = helper.extractLongNamed("transactionProcessingStrategyId", element, parametersPassedInCommand);
|
||||
final String externalId = helper.extractStringNamed("externalId", element, parametersPassedInCommand);
|
||||
final BigDecimal principal = helper.extractBigDecimalNamed("principal", element, parametersPassedInCommand);
|
||||
final BigDecimal inArrearsToleranceValue = helper.extractBigDecimalNamed("inArrearsTolerance", element, parametersPassedInCommand);
|
||||
final BigDecimal interestRatePerPeriod = helper.extractBigDecimalNamed("interestRatePerPeriod", element, parametersPassedInCommand);
|
||||
|
||||
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);
|
||||
final Integer repaymentEvery = helper.extractIntegerNamed("repaymentEvery", element, parametersPassedInCommand);
|
||||
final Integer numberOfRepayments = helper.extractIntegerNamed("numberOfRepayments", element, parametersPassedInCommand);
|
||||
final Integer repaymentFrequencyType = helper.extractIntegerNamed("repaymentFrequencyType", element, parametersPassedInCommand);
|
||||
final Integer loanTermFrequency = helper.extractIntegerNamed("loanTermFrequency", element, parametersPassedInCommand);
|
||||
final Integer loanTermFrequencyType = helper.extractIntegerNamed("loanTermFrequencyType", element, parametersPassedInCommand);
|
||||
final Integer interestRateFrequencyType = helper.extractIntegerNamed("interestRateFrequencyType", element, parametersPassedInCommand);
|
||||
final Integer amortizationType = helper.extractIntegerNamed("amortizationType", element, parametersPassedInCommand);
|
||||
final Integer interestType = helper.extractIntegerNamed("interestType", element, parametersPassedInCommand);
|
||||
final Integer interestCalculationPeriodType = helper.extractIntegerNamed("interestCalculationPeriodType", element, parametersPassedInCommand);
|
||||
|
||||
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);
|
||||
final LocalDate expectedDisbursementDate = helper.extractLocalDateNamed("expectedDisbursementDate", element, parametersPassedInCommand);
|
||||
final LocalDate repaymentsStartingFromDate = helper.extractLocalDateNamed("repaymentsStartingFromDate", element, parametersPassedInCommand);
|
||||
final LocalDate interestChargedFromDate = helper.extractLocalDateNamed("interestChargedFromDate", element, parametersPassedInCommand);
|
||||
final LocalDate submittedOnDate = helper.extractLocalDateNamed("submittedOnDate", element, parametersPassedInCommand);
|
||||
|
||||
final String submittedOnNote = helper.extractStringNamed("submittedOnNote", element, modifiedParameters);
|
||||
final String submittedOnNote = helper.extractStringNamed("submittedOnNote", element, parametersPassedInCommand);
|
||||
|
||||
LoanChargeCommand[] charges = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
if (object.has("charges") && object.get("charges").isJsonArray()) {
|
||||
modifiedParameters.add("charges");
|
||||
JsonArray array = object.get("charges").getAsJsonArray();
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = helper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = helper.extractLocaleParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.has("charges") && topLevelJsonElement.get("charges").isJsonArray()) {
|
||||
|
||||
parametersPassedInCommand.add("charges");
|
||||
final JsonArray array = topLevelJsonElement.get("charges").getAsJsonArray();
|
||||
charges = new LoanChargeCommand[array.size()];
|
||||
for (int i=0; i<array.size(); i++) {
|
||||
JsonObject loanCharge = array.get(i).getAsJsonObject();
|
||||
Map<String, Object> chargeRequestMap = gsonConverter.fromJson(loanCharge, typeOfMap);
|
||||
if (requestMap.containsKey("locale")){
|
||||
chargeRequestMap.put("locale", requestMap.get("locale"));
|
||||
}
|
||||
Set<String> chargeModifiedParameters = new HashSet<String>();
|
||||
|
||||
final JsonObject loanChargeElement = array.get(i).getAsJsonObject();
|
||||
final Set<String> chargeModifiedParameters = new HashSet<String>();
|
||||
|
||||
Long id = extractLongParameter("id", chargeRequestMap, chargeModifiedParameters);
|
||||
Long chargeId = extractLongParameter("chargeId", chargeRequestMap, chargeModifiedParameters);
|
||||
BigDecimal amount = extractBigDecimalParameter("amount", chargeRequestMap, chargeModifiedParameters);
|
||||
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", chargeRequestMap, chargeModifiedParameters);
|
||||
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", chargeRequestMap, chargeModifiedParameters);
|
||||
|
||||
charges[i] = new LoanChargeCommand(chargeModifiedParameters, id, null,chargeId, amount, chargeTimeType, chargeCalculationType);
|
||||
final Long id = helper.extractLongNamed("id", loanChargeElement, chargeModifiedParameters);
|
||||
final Long chargeId = helper.extractLongNamed("chargeId", loanChargeElement, chargeModifiedParameters);
|
||||
final BigDecimal amount = helper.extractBigDecimalNamed("amount", loanChargeElement, locale, chargeModifiedParameters);
|
||||
final Integer chargeTimeType = helper.extractIntegerNamed("chargeTimeType", loanChargeElement, locale, chargeModifiedParameters);
|
||||
final Integer chargeCalculationType = helper.extractIntegerNamed("chargeCalculationType", loanChargeElement, locale, chargeModifiedParameters);
|
||||
final LocalDate specifiedDueDate = helper.extractLocalDateNamed("specifiedDueDate", loanChargeElement, dateFormat, locale, parametersPassedInCommand);
|
||||
|
||||
charges[i] = new LoanChargeCommand(chargeModifiedParameters, id, null, chargeId, amount, chargeTimeType, chargeCalculationType, specifiedDueDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LoanApplicationCommand(modifiedParameters,
|
||||
return new LoanApplicationCommand(parametersPassedInCommand,
|
||||
resourceIdentifier, clientId, groupId, productId, externalId, fundId, transactionProcessingStrategyId,
|
||||
submittedOnDate, submittedOnNote,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
|
||||
@@ -535,8 +536,9 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
inArrearsToleranceValue, charges,loanOfficerId);
|
||||
}
|
||||
|
||||
// FIXME - KW - charges
|
||||
@Override
|
||||
public LoanChargeCommand convertJsonToLoanChargeCommand(Long loanChargeId, Long loanId, String json) {
|
||||
public LoanChargeCommand convertJsonToLoanChargeCommand(final Long loanChargeId, final Long loanId, final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
@@ -544,8 +546,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
|
||||
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
Set<String> supportedParams = new HashSet<String>(
|
||||
Arrays.asList("chargeId", "amount", "chargeTimeType", "chargeCalculationType", "locale")
|
||||
final Set<String> supportedParams = new HashSet<String>(
|
||||
Arrays.asList("chargeId", "amount", "chargeTimeType", "chargeCalculationType", "specifiedDueDate", "locale", "dateFormat")
|
||||
);
|
||||
|
||||
checkForUnsupportedParameters(requestMap, supportedParams);
|
||||
@@ -556,9 +558,10 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters);
|
||||
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters);
|
||||
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", requestMap, modifiedParameters);
|
||||
final LocalDate submittedOnDate = extractLocalDateParameter("specifiedDueDate", requestMap, modifiedParameters);
|
||||
|
||||
return new LoanChargeCommand(modifiedParameters, loanChargeId, loanId, chargeId, amount,
|
||||
chargeTimeType, chargeCalculationType);
|
||||
chargeTimeType, chargeCalculationType, submittedOnDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+100
-187
@@ -62,8 +62,24 @@ public class JsonParserHelper {
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
public BigDecimal extractBigDecimalNamed(final String parameterName,
|
||||
final JsonElement element, final Set<String> modifiedParameters) {
|
||||
public BigDecimal extractBigDecimalNamed(
|
||||
final String parameterName,
|
||||
final JsonElement element,
|
||||
final Set<String> modifiedParameters) {
|
||||
BigDecimal value = null;
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject object = element.getAsJsonObject();
|
||||
final Locale locale = extractLocaleValue(object);
|
||||
value = extractBigDecimalNamed(parameterName, object, locale, modifiedParameters);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public BigDecimal extractBigDecimalNamed(
|
||||
final String parameterName,
|
||||
final JsonObject element,
|
||||
final Locale locale,
|
||||
final Set<String> modifiedParameters) {
|
||||
BigDecimal value = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
@@ -74,68 +90,115 @@ public class JsonParserHelper {
|
||||
.getAsJsonPrimitive();
|
||||
String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
value = convertFrom(valueAsString, parameterName,
|
||||
extractLocaleValue(object));
|
||||
value = convertFrom(valueAsString, parameterName, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public Integer extractIntegerNamed(final String parameterName,
|
||||
final JsonElement element, final Set<String> modifiedParameters) {
|
||||
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()) {
|
||||
final JsonObject object = element.getAsJsonObject();
|
||||
final Locale locale = extractLocaleValue(object);
|
||||
value = extractIntegerNamed(parameterName, object, locale, modifiedParameters);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public Integer extractIntegerNamed(
|
||||
final String parameterName,
|
||||
final JsonElement element,
|
||||
final Locale locale,
|
||||
final Set<String> modifiedParameters) {
|
||||
Integer value = null;
|
||||
if (element.isJsonObject()) {
|
||||
final 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));
|
||||
value = convertToInteger(valueAsString, parameterName, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String extractDateFormatParameter(final JsonObject element) {
|
||||
String value = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
|
||||
public LocalDate extractLocalDateNamed(final String parameterName,
|
||||
final JsonElement element, final Set<String> modifiedParameters) {
|
||||
final String dateFormatParameter = "dateFormat";
|
||||
if (object.has(dateFormatParameter)
|
||||
&& object.get(dateFormatParameter).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = object.get(dateFormatParameter).getAsJsonPrimitive();
|
||||
value = primitive.getAsString();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public Locale extractLocaleParameter(final JsonObject element) {
|
||||
Locale clientApplicationLocale = null;
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
|
||||
String locale = null;
|
||||
final String localeParameter = "locale";
|
||||
if (object.has(localeParameter)
|
||||
&& object.get(localeParameter).isJsonPrimitive()) {
|
||||
final JsonPrimitive primitive = object.get(localeParameter).getAsJsonPrimitive();
|
||||
locale = primitive.getAsString();
|
||||
clientApplicationLocale = localeFromString(locale);
|
||||
}
|
||||
}
|
||||
return clientApplicationLocale;
|
||||
}
|
||||
|
||||
public LocalDate extractLocalDateNamed(
|
||||
final String parameterName,
|
||||
final JsonElement element,
|
||||
final Set<String> parametersPassedInCommand) {
|
||||
|
||||
LocalDate value = null;
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
|
||||
final String dateFormat = extractDateFormatParameter(object);
|
||||
final Locale clientApplicationLocale = extractLocaleParameter(object);
|
||||
value = extractLocalDateNamed(parameterName, object, dateFormat, clientApplicationLocale, parametersPassedInCommand);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public LocalDate extractLocalDateNamed(
|
||||
final String parameterName,
|
||||
final JsonObject element,
|
||||
final String dateFormat,
|
||||
final Locale clientApplicationLocale,
|
||||
final Set<String> parametersPassedInCommand) {
|
||||
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();
|
||||
}
|
||||
String locale = null;
|
||||
Locale clientApplicationLocale = null;
|
||||
final String localeParameter = "locale";
|
||||
if (object.has(localeParameter)
|
||||
&& object.get(localeParameter).isJsonPrimitive()) {
|
||||
JsonPrimitive primitive = object.get(localeParameter)
|
||||
.getAsJsonPrimitive();
|
||||
locale = primitive.getAsString();
|
||||
clientApplicationLocale = localeFromString(locale);
|
||||
}
|
||||
|
||||
if (object.has(parameterName)
|
||||
&& object.get(parameterName).isJsonPrimitive()) {
|
||||
modifiedParameters.add(parameterName);
|
||||
JsonPrimitive primitive = object.get(parameterName)
|
||||
.getAsJsonPrimitive();
|
||||
|
||||
parametersPassedInCommand.add(parameterName);
|
||||
|
||||
final JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
|
||||
final String valueAsString = primitive.getAsString();
|
||||
if (StringUtils.isNotBlank(valueAsString)) {
|
||||
value = convertFrom(valueAsString, parameterName,
|
||||
dateFormat, clientApplicationLocale);
|
||||
value = convertFrom(valueAsString, parameterName, dateFormat, clientApplicationLocale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,156 +419,6 @@ public class JsonParserHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
|
||||
+5
-1
@@ -3,7 +3,8 @@ package org.mifosng.platform.charge.domain;
|
||||
public enum ChargeTimeType {
|
||||
|
||||
INVALID(0, "chargeTimeType.invalid"),
|
||||
DISBURSEMENT(1, "chargeTimeType.disbursement");
|
||||
DISBURSEMENT(1, "chargeTimeType.disbursement"),
|
||||
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -27,6 +28,9 @@ public enum ChargeTimeType {
|
||||
case 1:
|
||||
chargeTimeType = DISBURSEMENT;
|
||||
break;
|
||||
case 2:
|
||||
chargeTimeType = SPECIFIED_DUE_DATE;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
|
||||
+5
-5
@@ -12,11 +12,11 @@ public class ChargeCommandValidator {
|
||||
|
||||
private final ChargeCommand command;
|
||||
|
||||
public ChargeCommandValidator(ChargeCommand command) {
|
||||
public ChargeCommandValidator(final ChargeCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void validateForCreate(){
|
||||
public void validateForCreate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
|
||||
@@ -25,7 +25,7 @@ public class ChargeCommandValidator {
|
||||
baseDataValidator.reset().parameter("amount").value(command.getAmount()).notNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).notBlank();
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(command.getChargeAppliesTo()).notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).notNull().inMinMaxRange(1, 2);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).notNull().inMinMaxRange(1, 4);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
@@ -33,7 +33,7 @@ public class ChargeCommandValidator {
|
||||
}
|
||||
}
|
||||
|
||||
public void validateForUpdate(){
|
||||
public void validateForUpdate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
|
||||
@@ -43,7 +43,7 @@ public class ChargeCommandValidator {
|
||||
baseDataValidator.reset().parameter("amount").value(command.getAmount()).ignoreIfNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("currencyCode").value(command.getCurrencyCode()).ignoreIfNull().notBlank();
|
||||
baseDataValidator.reset().parameter("chargeAppliesTo").value(command.getChargeAppliesTo()).ignoreIfNull().notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().notNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().notNull().inMinMaxRange(1, 2);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().notNull().inMinMaxRange(1, 4);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
|
||||
+1
-1
@@ -33,6 +33,6 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveCollectionTimeTypes() {
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT));
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -11,12 +11,15 @@ public class ChargeEnumerations {
|
||||
return chargeTimeType(ChargeTimeType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData chargeTimeType(ChargeTimeType type){
|
||||
public static EnumOptionData chargeTimeType(final ChargeTimeType type){
|
||||
EnumOptionData optionData = null;
|
||||
switch (type){
|
||||
case DISBURSEMENT:
|
||||
optionData = new EnumOptionData(ChargeTimeType.DISBURSEMENT.getValue().longValue(), ChargeTimeType.DISBURSEMENT.getCode(), "Disbursement");
|
||||
break;
|
||||
case SPECIFIED_DUE_DATE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.SPECIFIED_DUE_DATE.getValue().longValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getCode(), "Specified due date");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
@@ -28,7 +31,7 @@ public class ChargeEnumerations {
|
||||
return chargeAppliesTo(ChargeAppliesTo.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData chargeAppliesTo(ChargeAppliesTo type){
|
||||
public static EnumOptionData chargeAppliesTo(final ChargeAppliesTo type){
|
||||
EnumOptionData optionData = null;
|
||||
switch (type){
|
||||
case LOAN:
|
||||
@@ -45,7 +48,7 @@ public class ChargeEnumerations {
|
||||
return chargeCalculationType(ChargeCalculationType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData chargeCalculationType(ChargeCalculationType type){
|
||||
public static EnumOptionData chargeCalculationType(final ChargeCalculationType type){
|
||||
EnumOptionData optionData = null;
|
||||
switch (type){
|
||||
case FLAT:
|
||||
|
||||
+5
-1
@@ -6,6 +6,7 @@ import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.data.ChargeData;
|
||||
import org.mifosng.platform.api.data.CurrencyData;
|
||||
import org.mifosng.platform.api.data.EnumOptionData;
|
||||
@@ -177,6 +178,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
"lc.amount as amountDue, lc.amount_paid_derived as amountPaid, lc.amount_outstanding_derived as amountOutstanding, " +
|
||||
"lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " +
|
||||
"lc.charge_time_enum as chargeTime, " +
|
||||
"lc.due_for_collection_as_of_date as dueAsOfDate, " +
|
||||
"lc.charge_calculation_enum as chargeCalculation, " +
|
||||
"c.currency_code as currencyCode, oc.name as currencyName, " +
|
||||
"oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " +
|
||||
@@ -213,10 +215,12 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final int chargeTime = rs.getInt("chargeTime");
|
||||
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
|
||||
|
||||
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
|
||||
|
||||
final int chargeCalculation = rs.getInt("chargeCalculation");
|
||||
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
|
||||
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, chargeCalculationType, percentageOf, amountPercentageAppliedTo);
|
||||
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -20,7 +20,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
private final ChargeRepository chargeRepository;
|
||||
|
||||
@Autowired
|
||||
public ChargeWritePlatformServiceJpaRepositoryImpl(PlatformSecurityContext context, ChargeRepository chargeRepository) {
|
||||
public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ChargeRepository chargeRepository) {
|
||||
this.context = context;
|
||||
this.chargeRepository = chargeRepository;
|
||||
}
|
||||
@@ -31,21 +31,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
ChargeCommandValidator validator = new ChargeCommandValidator(command);
|
||||
final ChargeCommandValidator validator = new ChargeCommandValidator(command);
|
||||
validator.validateForCreate();
|
||||
|
||||
ChargeAppliesTo chargeAppliesTo = ChargeAppliesTo.fromInt(command.getChargeAppliesTo());
|
||||
ChargeTimeType chargeTimeType = ChargeTimeType.fromInt(command.getChargeTimeType());
|
||||
ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command.getChargeCalculationType());
|
||||
final ChargeAppliesTo chargeAppliesTo = ChargeAppliesTo.fromInt(command.getChargeAppliesTo());
|
||||
final ChargeTimeType chargeTimeType = ChargeTimeType.fromInt(command.getChargeTimeType());
|
||||
final ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command.getChargeCalculationType());
|
||||
|
||||
Charge charge = Charge.createNew(command.getName(), command.getAmount(), command.getCurrencyCode(),
|
||||
final Charge charge = Charge.createNew(command.getName(), command.getAmount(), command.getCurrencyCode(),
|
||||
chargeAppliesTo, chargeTimeType, chargeCalculationType, command.isActive());
|
||||
|
||||
this.chargeRepository.saveAndFlush(charge);
|
||||
|
||||
return charge.getId();
|
||||
} catch (DataIntegrityViolationException dve) {
|
||||
handleFundDataIntegrityIssues(command, dve);
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return Long.valueOf(-1);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
validator.validateForUpdate();
|
||||
|
||||
final Long chargeId = command.getId();
|
||||
Charge chargeForUpdate = this.chargeRepository.findOne(chargeId);
|
||||
final Charge chargeForUpdate = this.chargeRepository.findOne(chargeId);
|
||||
|
||||
if (chargeForUpdate == null){
|
||||
throw new ChargeNotFoundException(chargeId);
|
||||
@@ -72,7 +72,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
|
||||
return chargeForUpdate.getId();
|
||||
} catch (DataIntegrityViolationException dve) {
|
||||
handleFundDataIntegrityIssues(command, dve);
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return Long.valueOf(-1);
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
Charge chargeForDelete = this.chargeRepository.findOne(chargeId);
|
||||
final Charge chargeForDelete = this.chargeRepository.findOne(chargeId);
|
||||
if (chargeForDelete == null || chargeForDelete.isDeleted()){
|
||||
throw new ChargeNotFoundException(chargeId);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||
*/
|
||||
private void handleFundDataIntegrityIssues(final ChargeCommand command, DataIntegrityViolationException dve) {
|
||||
private void handleDataIntegrityIssues(final ChargeCommand command, final DataIntegrityViolationException dve) {
|
||||
|
||||
Throwable realCause = dve.getMostSpecificCause();
|
||||
if (realCause.getMessage().contains("name")) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Money implements Comparable<Money>, Serializable {
|
||||
}
|
||||
|
||||
public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) {
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), newAmount);
|
||||
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), defaultToZeroIfNull(newAmount));
|
||||
}
|
||||
|
||||
public static Money zero(final MonetaryCurrency currency) {
|
||||
@@ -56,13 +56,19 @@ public class Money implements Comparable<Money>, Serializable {
|
||||
private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount) {
|
||||
this.currencyCode = currencyCode;
|
||||
this.currencyDigitsAfterDecimal = digitsAfterDecimal;
|
||||
if (amount != null) {
|
||||
BigDecimal amountStripped = amount.stripTrailingZeros();
|
||||
this.amount = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
|
||||
} else {
|
||||
this.amount = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
final BigDecimal amountZeroed = defaultToZeroIfNull(amount);
|
||||
BigDecimal amountStripped = amountZeroed.stripTrailingZeros();
|
||||
this.amount = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
|
||||
}
|
||||
|
||||
private static BigDecimal defaultToZeroIfNull(final BigDecimal value) {
|
||||
BigDecimal result = BigDecimal.ZERO;
|
||||
if (value != null) {
|
||||
result = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Money copy() {
|
||||
return new Money(this.currencyCode, this.currencyDigitsAfterDecimal, this.amount.stripTrailingZeros());
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.mifosng.platform.exceptions;
|
||||
|
||||
/**
|
||||
* {@link AbstractPlatformDomainRuleException} thrown when currency mismatch occurs
|
||||
*/
|
||||
public class LoanChargeWithoutMandatoryFieldException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public LoanChargeWithoutMandatoryFieldException(final String entity, final String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg."+entity+"."+postFix+".cannot.be.blank", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
|
||||
}
|
||||
+15
-1
@@ -1,5 +1,6 @@
|
||||
package org.mifosng.platform.infrastructure;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
@@ -36,7 +37,20 @@ public class JdbcSupport {
|
||||
return (Long) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Long.class);
|
||||
}
|
||||
|
||||
public static Integer getInteger(ResultSet rs, String columnName) throws SQLException {
|
||||
public static Integer getInteger(final ResultSet rs, final String columnName) throws SQLException {
|
||||
return (Integer) JdbcUtils.getResultSetValue(rs, rs.findColumn(columnName), Integer.class);
|
||||
}
|
||||
|
||||
public static BigDecimal getBigDecimalDefaultToZeroIfNull(final ResultSet rs, final String columnName) throws SQLException {
|
||||
final BigDecimal value = rs.getBigDecimal(columnName);
|
||||
return defaultToZeroIfNull(value);
|
||||
}
|
||||
|
||||
private static BigDecimal defaultToZeroIfNull(final BigDecimal value) {
|
||||
BigDecimal result = BigDecimal.ZERO;
|
||||
if (value != null) {
|
||||
result = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +263,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.charges.add(loanCharge);
|
||||
|
||||
updateTotalChargesDueAtDisbursement();
|
||||
|
||||
// reprocess loan schedule based on charge been removed.
|
||||
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, this.charges, this.loanTransactions);
|
||||
}
|
||||
|
||||
public void removeLoanCharge(final LoanCharge loanCharge) {
|
||||
@@ -270,6 +274,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
if (removed) {
|
||||
updateTotalChargesDueAtDisbursement();
|
||||
}
|
||||
|
||||
// reprocess loan schedule based on charge been removed.
|
||||
LoanScheduleWrapper wrapper = new LoanScheduleWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, this.charges, this.loanTransactions);
|
||||
}
|
||||
|
||||
public void updateLoanCharge(final LoanCharge loanCharge, final LoanChargeCommand command) {
|
||||
@@ -373,7 +381,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this, scheduledLoanInstallment.periodNumber(),
|
||||
scheduledLoanInstallment.periodDueDate(),
|
||||
scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue());
|
||||
scheduledLoanInstallment.interestDue(),
|
||||
scheduledLoanInstallment.chargesDue());
|
||||
this.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.mifosng.platform.loan.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -11,11 +12,15 @@ import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
||||
import org.mifosng.platform.charge.domain.Charge;
|
||||
import org.mifosng.platform.charge.domain.ChargeCalculationType;
|
||||
import org.mifosng.platform.charge.domain.ChargeTimeType;
|
||||
import org.mifosng.platform.exceptions.LoanChargeWithoutMandatoryFieldException;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@@ -32,6 +37,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "charge_time_enum", nullable = false)
|
||||
private Integer chargeTime;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "due_for_collection_as_of_date")
|
||||
private Date dueForCollectionAsOfDate;
|
||||
|
||||
@Column(name = "charge_calculation_enum")
|
||||
private Integer chargeCalculation;
|
||||
@@ -80,13 +89,25 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.loan = null;
|
||||
this.charge = chargeDefinition;
|
||||
|
||||
if (command.isChargeTimeTypeChanged()){
|
||||
if (command.isChargeTimeTypeChanged()) {
|
||||
this.chargeTime = command.getChargeTimeType();
|
||||
} else {
|
||||
this.chargeTime = chargeDefinition.getChargeTime();
|
||||
}
|
||||
|
||||
if (ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE)) {
|
||||
|
||||
if (command.getSpecifiedDueDate() == null) {
|
||||
final String defaultUserMessage = "Loan charge is missing specified due date";
|
||||
throw new LoanChargeWithoutMandatoryFieldException("loancharge", "specifiedDueDate", defaultUserMessage, command.getId(), chargeDefinition.getName());
|
||||
}
|
||||
|
||||
this.dueForCollectionAsOfDate = command.getSpecifiedDueDate().toDate();
|
||||
} else {
|
||||
this.dueForCollectionAsOfDate = null;
|
||||
}
|
||||
|
||||
if (command.isChargeCalculationTypeChanged()){
|
||||
if (command.isChargeCalculationTypeChanged()) {
|
||||
this.chargeCalculation = command.getChargeCalculationType();
|
||||
} else {
|
||||
this.chargeCalculation = chargeDefinition.getChargeCalculation();
|
||||
@@ -110,6 +131,20 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
} else {
|
||||
this.chargeTime = chargeDefinition.getChargeTime();
|
||||
}
|
||||
|
||||
if (ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE)) {
|
||||
|
||||
if (command.getSpecifiedDueDate() == null) {
|
||||
final String defaultUserMessage = "Loan charge is missing specified due date";
|
||||
throw new LoanChargeWithoutMandatoryFieldException(
|
||||
"loancharge", "specifiedDueDate", defaultUserMessage,
|
||||
command.getId(), chargeDefinition.getName());
|
||||
}
|
||||
|
||||
this.dueForCollectionAsOfDate = command.getSpecifiedDueDate().toDate();
|
||||
} else {
|
||||
this.dueForCollectionAsOfDate = null;
|
||||
}
|
||||
|
||||
if (command.isChargeCalculationTypeChanged()){
|
||||
this.chargeCalculation = command.getChargeCalculationType();
|
||||
@@ -226,54 +261,24 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.DISBURSEMENT);
|
||||
}
|
||||
|
||||
// public BigDecimal calculateMonetaryAmount(final Money principalDisbursed) {
|
||||
// BigDecimal calculatedAmount = BigDecimal.ZERO;
|
||||
//
|
||||
// switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
// case INVALID:
|
||||
// calculatedAmount = this.amount;
|
||||
// break;
|
||||
// case FLAT:
|
||||
// calculatedAmount = this.amount;
|
||||
// break;
|
||||
// case PERCENT_OF_AMOUNT:
|
||||
// this.amountPercentageAppliedTo = principalDisbursed.getAmount();
|
||||
// this.amount = percentageOf(this.amountPercentageAppliedTo, this.percentage);
|
||||
// this.amountPaid = null;
|
||||
// this.amountOutstanding = calculateOutstanding();
|
||||
// calculatedAmount = this.amount;
|
||||
// break;
|
||||
// case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
// this.percentage = null;
|
||||
// this.amount = null;
|
||||
// this.amountPercentageAppliedTo = null;
|
||||
// this.amountPaid = null;
|
||||
// this.amountOutstanding = null;
|
||||
// break;
|
||||
// case PERCENT_OF_INTEREST:
|
||||
// this.percentage = null;
|
||||
// this.amount = null;
|
||||
// this.amountPercentageAppliedTo = null;
|
||||
// this.amountPaid = null;
|
||||
// this.amountOutstanding = null;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// return calculatedAmount;
|
||||
// }
|
||||
//
|
||||
// public BigDecimal calculateMonetaryAmount() {
|
||||
// return calculateMonetaryAmount(this.loan.getPrincpal());
|
||||
// }
|
||||
|
||||
private boolean isGreaterThanZero(final BigDecimal value) {
|
||||
return value.compareTo(BigDecimal.ZERO) == 1;
|
||||
}
|
||||
|
||||
public LoanChargeCommand toData() {
|
||||
Set<String> modifiedParameters = new HashSet<String>();
|
||||
public LoanChargeCommand toCommand() {
|
||||
|
||||
final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate();
|
||||
final Set<String> modifiedParameters = new HashSet<String>();
|
||||
return new LoanChargeCommand(modifiedParameters, this.getId(), this.loan.getId(), this.charge.getId(),
|
||||
this.amount, this.chargeTime, this.chargeCalculation);
|
||||
this.amount, this.chargeTime, this.chargeCalculation, specifiedDueDate);
|
||||
}
|
||||
|
||||
private LocalDate getDueForCollectionAsOfLocalDate() {
|
||||
LocalDate specifiedDueDate = null;
|
||||
if (this.dueForCollectionAsOfDate != null) {
|
||||
specifiedDueDate = new LocalDate(this.dueForCollectionAsOfDate);
|
||||
}
|
||||
return specifiedDueDate;
|
||||
}
|
||||
|
||||
private boolean determineIfFullyPaid() {
|
||||
@@ -326,4 +331,17 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public boolean hasLoanIdentifiedBy(final Long loanId) {
|
||||
return this.loan.hasIdentifyOf(loanId);
|
||||
}
|
||||
|
||||
public boolean isDueForCollectionBetween(final LocalDate fromNotInclusive, final LocalDate toInclusive) {
|
||||
final LocalDate specifiedDueDate = getDueForCollectionAsOfLocalDate();
|
||||
|
||||
return specifiedDueDateFallsWithinPeriod(fromNotInclusive, toInclusive, specifiedDueDate);
|
||||
}
|
||||
|
||||
private boolean specifiedDueDateFallsWithinPeriod(
|
||||
final LocalDate fromNotInclusive,
|
||||
final LocalDate toInclusive,
|
||||
final LocalDate specifiedDueDate) {
|
||||
return specifiedDueDate != null && fromNotInclusive.isBefore(specifiedDueDate) && (toInclusive.isAfter(specifiedDueDate) || toInclusive.isEqual(specifiedDueDate));
|
||||
}
|
||||
}
|
||||
+75
-14
@@ -29,25 +29,49 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
private final Integer installmentNumber;
|
||||
|
||||
@Column(name = "principal_amount", scale=6, precision=19)
|
||||
private BigDecimal principal = BigDecimal.ZERO;
|
||||
private BigDecimal principal;
|
||||
|
||||
@Column(name = "interest_amount", scale=6, precision=19)
|
||||
private BigDecimal interest = BigDecimal.ZERO;
|
||||
private BigDecimal interest;
|
||||
|
||||
@Column(name = "fee_charges_amount", scale=6, precision=19)
|
||||
private BigDecimal feeCharges;
|
||||
|
||||
@Column(name = "fee_charges_completed_derived", scale=6, precision=19)
|
||||
private BigDecimal feeChargesCompleted;
|
||||
|
||||
@Column(name = "fee_charges_writtenoff_derived", scale=6, precision=19)
|
||||
private BigDecimal feeChargesWrittenOff;
|
||||
|
||||
@Column(name = "fee_charges_waived_derived", scale=6, precision=19)
|
||||
private BigDecimal feeChargesWaived;
|
||||
|
||||
@Column(name = "penalty_charges_amount", scale=6, precision=19)
|
||||
private BigDecimal penaltyCharges;
|
||||
|
||||
@Column(name = "penalty_charges_completed_derived", scale=6, precision=19)
|
||||
private BigDecimal penaltyChargesCompleted;
|
||||
|
||||
@Column(name = "penalty_charges_writtenoff_derived", scale=6, precision=19)
|
||||
private BigDecimal penaltyChargesWrittenOff;
|
||||
|
||||
@Column(name = "penalty_charges_waived_derived", scale=6, precision=19)
|
||||
private BigDecimal penaltyChargesWaived;
|
||||
|
||||
@Column(name = "principal_completed_derived", scale=6, precision=19)
|
||||
private BigDecimal principalCompleted = BigDecimal.ZERO;
|
||||
private BigDecimal principalCompleted;
|
||||
|
||||
@Column(name = "principal_writtenoff_derived", scale=6, precision=19)
|
||||
private BigDecimal principalWrittenOff = BigDecimal.ZERO;
|
||||
private BigDecimal principalWrittenOff;
|
||||
|
||||
@Column(name = "interest_completed_derived", scale = 6, precision = 19)
|
||||
private BigDecimal interestCompleted = BigDecimal.ZERO;
|
||||
private BigDecimal interestCompleted;
|
||||
|
||||
@Column(name = "interest_waived_derived", scale = 6, precision = 19)
|
||||
private BigDecimal interestWaived = BigDecimal.ZERO;
|
||||
private BigDecimal interestWaived;
|
||||
|
||||
@Column(name = "interest_writtenoff_derived", scale=6, precision=19)
|
||||
private BigDecimal interestWrittenOff = BigDecimal.ZERO;
|
||||
private BigDecimal interestWrittenOff;
|
||||
|
||||
@Column(name="completed_derived")
|
||||
private boolean completed;
|
||||
@@ -67,19 +91,31 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber,
|
||||
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest) {
|
||||
public LoanRepaymentScheduleInstallment(
|
||||
final Loan loan,
|
||||
final Integer installmentNumber,
|
||||
final LocalDate dueDate,
|
||||
final BigDecimal principal,
|
||||
final BigDecimal interest,
|
||||
final BigDecimal feeCharges) {
|
||||
this.loan = loan;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.dueDate = dueDate.toDateMidnight().toDate();
|
||||
this.principal = principal;
|
||||
this.principalCompleted = BigDecimal.ZERO;
|
||||
this.interest = interest;
|
||||
this.interestCompleted = BigDecimal.ZERO;
|
||||
this.principal = defaultToNullIfZero(principal);
|
||||
this.interest = defaultToNullIfZero(interest);
|
||||
this.feeCharges = defaultToNullIfZero(feeCharges);
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
public Loan getLoan() {
|
||||
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
||||
BigDecimal result = value;
|
||||
if (BigDecimal.ZERO.compareTo(value) == 0) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Loan getLoan() {
|
||||
return this.loan;
|
||||
}
|
||||
|
||||
@@ -128,6 +164,27 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
final Money interestAccountedFor = getInterestCompleted(currency).plus(getInterestWaived(currency)).plus(getInterestWrittenOff(currency));
|
||||
return getInterest(currency).minus(interestAccountedFor);
|
||||
}
|
||||
|
||||
public Money getFeeCharges(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeCharges);
|
||||
}
|
||||
|
||||
public Money getFeeChargesCompleted(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeChargesCompleted);
|
||||
}
|
||||
|
||||
public Money getFeeChargesWaived(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeChargesWaived);
|
||||
}
|
||||
|
||||
public Money getFeeChargesWrittenOff(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeChargesWrittenOff);
|
||||
}
|
||||
|
||||
public Money getFeeChargesOutstanding(final MonetaryCurrency currency) {
|
||||
final Money feeChargesAccountedFor = getFeeChargesCompleted(currency).plus(getFeeChargesWaived(currency)).plus(getFeeChargesWrittenOff(currency));
|
||||
return getFeeCharges(currency).minus(feeChargesAccountedFor);
|
||||
}
|
||||
|
||||
public boolean isInterestDue(final MonetaryCurrency currency) {
|
||||
return getInterestOutstanding(currency).isGreaterThanZero();
|
||||
@@ -247,4 +304,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
public boolean isOverdueOn(final LocalDate transactionDate) {
|
||||
return this.getDueDate().isBefore(transactionDate);
|
||||
}
|
||||
|
||||
public void updateChargePortion(final Money feeChargesDue) {
|
||||
this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount());
|
||||
}
|
||||
}
|
||||
+42
@@ -1,7 +1,9 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.currency.domain.Money;
|
||||
|
||||
@@ -24,4 +26,44 @@ public class LoanScheduleWrapper {
|
||||
|
||||
return cumulativeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME - this is not a side effect free approach so move this into a specific wrapper than makes this obvious from the read-only wrapper.
|
||||
* @param localDate
|
||||
*/
|
||||
public void reprocess(
|
||||
final MonetaryCurrency currency,
|
||||
final LocalDate disbursementDate,
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentPeriods,
|
||||
final Set<LoanCharge> loanCharges,
|
||||
final List<LoanTransaction> transactions) {
|
||||
|
||||
LocalDate startDate = disbursementDate;
|
||||
for (LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||
|
||||
// FIXME - kw - also need to handle case where some of these charges are paid/writtenoff/waived so that those components are updated on repayment period.
|
||||
final Money feeChargesDueForRepaymentPeriod = cumulativeChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
|
||||
// if (feeChargesDueForRepaymentPeriod.isGreaterThanZero()) {
|
||||
period.updateChargePortion(feeChargesDueForRepaymentPeriod);
|
||||
// }
|
||||
startDate = period.getDueDate();
|
||||
}
|
||||
}
|
||||
|
||||
private Money cumulativeChargesDueWithin(
|
||||
final LocalDate periodStart,
|
||||
final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
}
|
||||
+11
@@ -83,6 +83,17 @@ public class CalculateLoanScheduleCommandValidator {
|
||||
}
|
||||
}
|
||||
|
||||
// if (command.getCharges() != null) {
|
||||
// for (LoanChargeCommand loanCharge : command.getCharges()) {
|
||||
// try {
|
||||
// LoanChargeCommandValidator validator = new LoanChargeCommandValidator(loanCharge);
|
||||
// validator.validateForCreate();
|
||||
// } catch (PlatformApiDataValidationException e) {
|
||||
// dataValidationErrors.addAll(e.getErrors());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ import org.mifosng.platform.api.commands.CalculateLoanScheduleCommand;
|
||||
|
||||
public interface CalculationPlatformService {
|
||||
|
||||
LoanScheduleData calculateLoanScheduleNew(CalculateLoanScheduleCommand command);
|
||||
LoanScheduleData calculateLoanSchedule(CalculateLoanScheduleCommand command);
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class CalculationPlatformServiceImpl implements CalculationPlatformServic
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleData calculateLoanScheduleNew(final CalculateLoanScheduleCommand command) {
|
||||
public LoanScheduleData calculateLoanSchedule(final CalculateLoanScheduleCommand command) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
|
||||
+23
-11
@@ -84,19 +84,21 @@ public class LoanAssembler {
|
||||
}
|
||||
|
||||
Client client = null;
|
||||
Group group = null;
|
||||
if (command.getClientId() != null) {
|
||||
client = this.clientRepository.findOne(command.getClientId());
|
||||
if (client == null || client.isDeleted()) {
|
||||
throw new ClientNotFoundException(command.getClientId());
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
Group group = null;
|
||||
if (command.getGroupId() != null) {
|
||||
group = this.groupRepository.findOne(command.getGroupId());
|
||||
if (group == null || group.isDeleted()) {
|
||||
throw new GroupNotFoundException(command.getGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final MonetaryCurrency currency = loanProduct.getCurrency();
|
||||
final Integer loanTermFrequency = command.getLoanTermFrequency();
|
||||
final PeriodFrequencyType loanTermFrequencyType = PeriodFrequencyType.fromInt(command.getLoanTermFrequencyType());
|
||||
@@ -113,24 +115,34 @@ public class LoanAssembler {
|
||||
// optionally, see if charges are associated with loan on creation (through loan product or by being directly added)
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), command.getPrincipal());
|
||||
|
||||
Loan loan;
|
||||
Loan loan = null;
|
||||
if (group != null) {
|
||||
loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, group, loanRepaymentScheduleDetail, loanCharges);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
}
|
||||
|
||||
if (client != null) {
|
||||
loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail, loanCharges);
|
||||
} else {
|
||||
loan = Loan.createNew(fund,loanOfficer, loanTransactionProcessingStrategy, loanProduct, group, loanRepaymentScheduleDetail, loanCharges);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
}
|
||||
|
||||
if (loan == null) {
|
||||
// FIXME - kw - put in appropriate exception here.
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
|
||||
|
||||
for (LoanSchedulePeriodData scheduledLoanInstallment : loanSchedule.getPeriods()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
|
||||
loan, scheduledLoanInstallment.periodNumber(),
|
||||
scheduledLoanInstallment.periodDueDate(),
|
||||
scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue());
|
||||
scheduledLoanInstallment.interestDue(),
|
||||
scheduledLoanInstallment.chargesDue());
|
||||
|
||||
loan.addRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-9
@@ -1,6 +1,5 @@
|
||||
package org.mifosng.platform.loan.service;
|
||||
|
||||
|
||||
import org.mifosng.platform.DataValidatorBuilder;
|
||||
import org.mifosng.platform.api.commands.LoanChargeCommand;
|
||||
import org.mifosng.platform.api.data.ApiParameterError;
|
||||
@@ -13,36 +12,42 @@ public class LoanChargeCommandValidator {
|
||||
|
||||
private final LoanChargeCommand command;
|
||||
|
||||
public LoanChargeCommandValidator(LoanChargeCommand command) {
|
||||
public LoanChargeCommandValidator(final LoanChargeCommand command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public void validateForCreate(){
|
||||
public void validateForCreate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
|
||||
|
||||
baseDataValidator.reset().parameter("chargeId").value(command.getChargeId()).notNull().longGreaterThanZero();
|
||||
baseDataValidator.reset().parameter("amount").value(command.getAmount()).ignoreIfNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().inMinMaxRange(1, 4);
|
||||
baseDataValidator.reset().parameter("amount").value(command.getAmount()).notNull().positiveAmount();
|
||||
// baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1, 2);
|
||||
//
|
||||
// if (command.getChargeTimeType().equals(Integer.valueOf(2))) {
|
||||
// // date must be provided
|
||||
// baseDataValidator.reset().parameter("specifiedDueDate").value(command.getSpecifiedDueDate()).notNull();
|
||||
// }
|
||||
//
|
||||
// baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().inMinMaxRange(1, 4);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateForUpdate(){
|
||||
public void validateForUpdate() {
|
||||
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
|
||||
|
||||
baseDataValidator.reset().parameter("amount").value(command.getAmount()).ignoreIfNull().positiveAmount();
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1, 1);
|
||||
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).ignoreIfNull().inMinMaxRange(1, 2);
|
||||
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).ignoreIfNull().inMinMaxRange(1, 4);
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-29
@@ -507,18 +507,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private static final class LoanScheduleMapper implements RowMapper<LoanSchedulePeriodData> {
|
||||
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanBalance;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
|
||||
public LoanScheduleMapper(final DisbursementData disbursement) {
|
||||
this.lastDueDate = disbursement.disbursementDate();
|
||||
this.outstandingLoanBalance = disbursement.amount();
|
||||
this.outstandingLoanPrincipalBalance = disbursement.amount();
|
||||
}
|
||||
|
||||
public String loanScheduleSchema() {
|
||||
|
||||
return " ls.loan_id as loanId, ls.installment as period, ls.duedate as dueDate, "
|
||||
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, "
|
||||
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff "
|
||||
+ " ls.interest_amount as interestDue, ls.interest_completed_derived as interestPaid, ls.interest_waived_derived as interestWaived, ls.interest_writtenoff_derived as interestWrittenOff, "
|
||||
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff "
|
||||
+ " from m_loan l "
|
||||
+ " join m_loan_repayment_schedule ls on ls.loan_id = l.id ";
|
||||
}
|
||||
@@ -529,47 +530,46 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Long loanId = rs.getLong("loanId");
|
||||
final Integer period = JdbcSupport.getInteger(rs, "period");
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||
final BigDecimal principalDue = rs.getBigDecimal("principalDue");
|
||||
final BigDecimal principalPaid = rs.getBigDecimal("principalPaid");
|
||||
BigDecimal principalWrittenOff = rs.getBigDecimal("principalWrittenOff");
|
||||
if (principalWrittenOff == null) {
|
||||
principalWrittenOff = BigDecimal.ZERO;
|
||||
}
|
||||
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
|
||||
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
|
||||
final BigDecimal principalWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalWrittenOff");
|
||||
|
||||
// TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns?
|
||||
final BigDecimal principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
|
||||
|
||||
final BigDecimal interestDue = rs.getBigDecimal("interestDue");
|
||||
final BigDecimal interestPaid = rs.getBigDecimal("interestPaid");
|
||||
BigDecimal interestWaived = rs.getBigDecimal("interestWaived");
|
||||
if (interestWaived == null) {
|
||||
interestWaived = BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal interestWrittenOff = rs.getBigDecimal("interestWrittenOff");
|
||||
if (interestWrittenOff == null) {
|
||||
interestWrittenOff = BigDecimal.ZERO;
|
||||
}
|
||||
final BigDecimal interestDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
|
||||
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
|
||||
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
|
||||
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
|
||||
|
||||
// TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns?
|
||||
final BigDecimal interestOutstanding = interestDue.subtract(interestPaid).subtract(interestWaived).subtract(interestWrittenOff);
|
||||
|
||||
final BigDecimal feeChargesDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue");
|
||||
final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid");
|
||||
final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
|
||||
final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff");
|
||||
|
||||
final BigDecimal feeChargesOutstanding = feeChargesDue.subtract(feeChargesPaid).subtract(feeChargesWaived).subtract(feeChargesWrittenOff);
|
||||
|
||||
final BigDecimal totalDueForPeriod = principalDue.add(interestDue);
|
||||
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid);
|
||||
final BigDecimal totalWaivedForPeriod = interestWaived;
|
||||
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff);
|
||||
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding);
|
||||
final BigDecimal totalDueForPeriod = principalDue.add(interestDue).add(feeChargesDue);
|
||||
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid);
|
||||
final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived);
|
||||
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff);
|
||||
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding);
|
||||
|
||||
final LocalDate fromDate = this.lastDueDate;
|
||||
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanBalance.subtract(principalDue);
|
||||
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
|
||||
|
||||
// update based on current period values
|
||||
this.lastDueDate = dueDate;
|
||||
outstandingLoanBalance = outstandingLoanBalance.subtract(principalDue);
|
||||
outstandingLoanPrincipalBalance = outstandingLoanPrincipalBalance.subtract(principalDue);
|
||||
|
||||
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate,
|
||||
principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, interestDue,
|
||||
interestPaid, interestWaived, interestWrittenOff, interestOutstanding, totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod,
|
||||
totalOutstandingForPeriod);
|
||||
principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan,
|
||||
interestDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
|
||||
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
|
||||
totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -162,7 +162,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LoanTransactionProcessingStrategy strategy = this.loanAssembler.findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
||||
final Set<LoanCharge> charges = this.loanChargeAssembler.assembleFrom(command.getCharges(), loanProduct.getCharges(), loan.getPrincpal().getAmount());
|
||||
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(command.toCalculateLoanScheduleCommand());
|
||||
final LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command.toCalculateLoanScheduleCommand());
|
||||
loan.modifyLoanApplication(command, client, loanProduct, fund, strategy, loanSchedule, charges, loanOfficer, defaultLoanLifecycleStateMachine());
|
||||
|
||||
this.loanRepository.save(loan);
|
||||
@@ -361,7 +361,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
Set<LoanCharge> loanCharges = loan.getCharges();
|
||||
List<LoanChargeCommand> commands = new ArrayList<LoanChargeCommand>();
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
commands.add(loanCharge.toData());
|
||||
commands.add(loanCharge.toCommand());
|
||||
}
|
||||
|
||||
LoanChargeCommand[] loanChargeCommands = commands.toArray(new LoanChargeCommand[commands.size()]);
|
||||
@@ -374,7 +374,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanTermFrequency, loanTermFrequencyType,
|
||||
actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanChargeCommands);
|
||||
|
||||
LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanScheduleNew(calculateCommand);
|
||||
LoanScheduleData loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand);
|
||||
|
||||
List<LoanRepaymentScheduleInstallment> modifiedLoanRepaymentSchedule = new ArrayList<LoanRepaymentScheduleInstallment>();
|
||||
|
||||
@@ -384,7 +384,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan, scheduledLoanPeriod.periodNumber(),
|
||||
scheduledLoanPeriod.periodDueDate(),
|
||||
scheduledLoanPeriod.principalDue(),
|
||||
scheduledLoanPeriod.interestDue());
|
||||
scheduledLoanPeriod.interestDue(),
|
||||
scheduledLoanPeriod.chargesDue());
|
||||
|
||||
modifiedLoanRepaymentSchedule.add(installment);
|
||||
}
|
||||
|
||||
-1
@@ -16,7 +16,6 @@ public interface AmortizationLoanScheduleGenerator {
|
||||
ApplicationCurrency applicationCurrency,
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate,
|
||||
LocalDate firstRepaymentDate,
|
||||
LocalDate interestCalculatedFrom,
|
||||
BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ public class DecliningBalanceMethodLoanScheduleGenerator implements LoanSchedule
|
||||
return generator.generate(applicationCurrency,
|
||||
loanScheduleInfo,
|
||||
disbursementDate,
|
||||
firstRepaymentDate,
|
||||
interestCalculatedFrom,
|
||||
periodInterestRateForRepaymentPeriod,
|
||||
idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
|
||||
+24
-5
@@ -31,7 +31,6 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
final ApplicationCurrency currency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
final LocalDate interestCalculatedFrom,
|
||||
final BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
@@ -60,7 +59,6 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
|
||||
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
// FIXME - KW - right now only charges at disbursement are supported.
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
|
||||
}
|
||||
@@ -119,15 +117,19 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
}
|
||||
}
|
||||
|
||||
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment);
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalForInstallment);
|
||||
|
||||
Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment);
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalForInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestForInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
interestForInstallment.getAmount(),
|
||||
chargesForInstallment.getAmount(),
|
||||
totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
@@ -153,4 +155,21 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
|
||||
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
private Money cumulativeChargesDueWithin(
|
||||
final LocalDate periodStart,
|
||||
final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
}
|
||||
+9
-5
@@ -27,7 +27,6 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
|
||||
final ApplicationCurrency currency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate firstRepaymentDate,
|
||||
final LocalDate interestCalculatedFrom,
|
||||
final BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
@@ -50,7 +49,6 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
|
||||
|
||||
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
// FIXME - KW - right now only charges at disbursement are supported.
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
|
||||
}
|
||||
@@ -100,15 +98,21 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
|
||||
}
|
||||
}
|
||||
|
||||
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment);
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalForInstallment);
|
||||
|
||||
// FIXME - KW - charges
|
||||
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
Money chargesForInstallment = Money.zero(monetaryCurrency);
|
||||
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment);
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalForInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestForInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
interestForInstallment.getAmount(),
|
||||
chargesForInstallment.getAmount(),
|
||||
totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
|
||||
+8
-3
@@ -65,7 +65,6 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
// FIXME - KW - right now only charges at disbursement are supported.
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
|
||||
}
|
||||
@@ -108,14 +107,20 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment);
|
||||
outstandingBalance = outstandingBalance.minus(principalPerInstallment);
|
||||
|
||||
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
Money chargesForInstallment = Money.zero(monetaryCurrency);
|
||||
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(chargesForInstallment);
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalPerInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestPerInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
interestPerInstallment.getAmount(),
|
||||
chargesForInstallment.getAmount(),
|
||||
totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
|
||||
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
|
||||
private Money principal = new MoneyBuilder().build();
|
||||
private Money interest = new MoneyBuilder().build();
|
||||
private Money feeCharges = new MoneyBuilder().build();
|
||||
private boolean completed = false;
|
||||
|
||||
public LoanRepaymentScheduleInstallmentBuilder(MonetaryCurrency currencyDetail) {
|
||||
@@ -21,7 +22,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment build() {
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, dueDate, principal.getAmount(), interest.getAmount());
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, dueDate, principal.getAmount(), interest.getAmount(), feeCharges.getAmount());
|
||||
if (completed) {
|
||||
installment.payPrincipalComponent(principal);
|
||||
installment.payInterestComponent(interest);
|
||||
|
||||
Reference in New Issue
Block a user