MIFOSX-90: work in progress on charges (fees and penalties) for loans suitable for gk-maarg. Split out charges on repayment schedule into fee charges and penalty charges. work outstanding on integrating payment processing to auto pay off charges (or auto penalise).

This commit is contained in:
Keith Woodlock
2012-10-31 11:08:54 +00:00
parent a946e207da
commit a3200a8bc7
30 changed files with 563 additions and 370 deletions
@@ -0,0 +1,8 @@
alter table m_loan_repayment_schedule
add column `fromdate` date DEFAULT NULL after `loan_id`;
alter table m_charge
add column `is_penalty` tinyint(1) NOT NULL DEFAULT '0' after `amount`;
alter table m_loan_charge
add column `is_penalty` tinyint(1) NOT NULL DEFAULT '0' after `charge_id`;
@@ -50,10 +50,10 @@ public class ChargesApiResource {
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveAllCharges(@Context final UriInfo uriInfo){ public String retrieveAllCharges(@Context final UriInfo uriInfo){
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveAllCharges(); final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveAllCharges();
return this.apiJsonSerializerService.serializeChargeDataToJson(prettyPrint, responseParameters, charges); return this.apiJsonSerializerService.serializeChargeDataToJson(prettyPrint, responseParameters, charges);
} }
@@ -63,16 +63,15 @@ public class ChargesApiResource {
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveCharge(@PathParam("chargeId") final Long chargeId, @Context final UriInfo uriInfo){ public String retrieveCharge(@PathParam("chargeId") final Long chargeId, @Context final UriInfo uriInfo){
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
ChargeData charge = this.chargeReadPlatformService.retrieveCharge(chargeId); ChargeData charge = this.chargeReadPlatformService.retrieveCharge(chargeId);
if (template) {
if (template){
ChargeData templateData = this.chargeReadPlatformService.retrieveNewChargeDetails(); ChargeData templateData = this.chargeReadPlatformService.retrieveNewChargeDetails();
charge = new ChargeData(charge, templateData); charge = new ChargeData(charge, templateData);
} }
@@ -84,11 +83,12 @@ public class ChargesApiResource {
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveNewChargeDetails(@Context final UriInfo uriInfo) { public String retrieveNewChargeDetails(@Context final UriInfo uriInfo) {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
ChargeData chargeData = this.chargeReadPlatformService.retrieveNewChargeDetails(); final ChargeData chargeData = this.chargeReadPlatformService.retrieveNewChargeDetails();
return this.apiJsonSerializerService.serializeChargeDataToJson(prettyPrint, responseParameters, chargeData); return this.apiJsonSerializerService.serializeChargeDataToJson(prettyPrint, responseParameters, chargeData);
} }
@@ -98,9 +98,9 @@ public class ChargesApiResource {
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public Response createCharge(final String jsonRequestBody){ public Response createCharge(final String jsonRequestBody){
ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(null, jsonRequestBody); final ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(null, jsonRequestBody);
Long chargeId = this.chargeWritePlatformService.createCharge(command); final Long chargeId = this.chargeWritePlatformService.createCharge(command);
return Response.ok().entity(new EntityIdentifier(chargeId)).build(); return Response.ok().entity(new EntityIdentifier(chargeId)).build();
} }
@@ -111,9 +111,9 @@ public class ChargesApiResource {
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public Response updateCharge(@PathParam("chargeId") final Long chargeId, final String jsonRequestBody){ public Response updateCharge(@PathParam("chargeId") final Long chargeId, final String jsonRequestBody){
ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(chargeId, jsonRequestBody); final ChargeCommand command = this.apiDataConversionService.convertJsonToChargeCommand(chargeId, jsonRequestBody);
Long entityId = this.chargeWritePlatformService.updateCharge(command); final Long entityId = this.chargeWritePlatformService.updateCharge(command);
return Response.ok().entity(new EntityIdentifier(entityId)).build(); return Response.ok().entity(new EntityIdentifier(entityId)).build();
} }
@@ -1,8 +1,6 @@
package org.mifosng.platform.api; package org.mifosng.platform.api;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -66,19 +64,6 @@ public class LoanProductsApiResource {
@Autowired @Autowired
private LoanDropdownReadPlatformService dropdownReadPlatformService; private LoanDropdownReadPlatformService dropdownReadPlatformService;
private final Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "name", "description",
"fundId", "fundName",
"transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance",
"numberOfRepayments", "repaymentEvery",
"interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType",
"amortizationType", "interestType", "interestCalculationPeriodType",
"charges",
"createdOn", "lastModifedOn")
);
@POST @POST
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@@ -98,9 +83,6 @@ public class LoanProductsApiResource {
public String retrieveAllLoanProducts(@Context final UriInfo uriInfo) { public String retrieveAllLoanProducts(@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final Collection<LoanProductData> products = this.loanProductReadPlatformService.retrieveAllLoanProducts(); final Collection<LoanProductData> products = this.loanProductReadPlatformService.retrieveAllLoanProducts();
@@ -115,13 +97,10 @@ public class LoanProductsApiResource {
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) { public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails(); LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails();
loanProduct = handleTemplate(loanProduct, responseParameters); loanProduct = handleTemplate(loanProduct);
return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct); return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct);
} }
@@ -133,15 +112,12 @@ public class LoanProductsApiResource {
public String retrieveLoanProductDetails(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) { public String retrieveLoanProductDetails(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
if (template) { if (template) {
loanProduct = handleTemplate(loanProduct, responseParameters); loanProduct = handleTemplate(loanProduct);
} }
return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct); return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct);
@@ -160,24 +136,22 @@ public class LoanProductsApiResource {
return apiJsonSerializerService.serializeEntityIdentifier(identifier); return apiJsonSerializerService.serializeEntityIdentifier(identifier);
} }
private LoanProductData handleTemplate(final LoanProductData productData, final Set<String> responseParameters) { private LoanProductData handleTemplate(final LoanProductData productData) {
responseParameters.addAll(Arrays.asList("currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", final boolean feeChargesOnly = true;
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions")); Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
chargeOptions.removeAll(productData.charges()); chargeOptions.removeAll(productData.charges());
List<CurrencyData> currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies(); final List<CurrencyData> currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies();
List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions(); final List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions(); final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions(); final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); final List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions(); final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds(); final Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies(); final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions, return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions); interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions);
@@ -58,14 +58,21 @@ public class LoanScheduleData {
private final BigDecimal cumulativeInterestOutstanding; private final BigDecimal cumulativeInterestOutstanding;
/** /**
* The cumulative total of all charges applied on the loan to date. * The cumulative total of all fee charges applied on the loan to date.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesToDate; private final BigDecimal cumulativeFeeChargesToDate;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesPaid; private final BigDecimal cumulativeFeeChargesPaid;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal cumulativeChargesOutstanding; private final BigDecimal cumulativeFeeChargesOutstanding;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesToDate;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesPaid;
@SuppressWarnings("unused")
private final BigDecimal cumulativePenaltyChargesOutstanding;
/** /**
* The cumulative total of all costs on the loan. * The cumulative total of all costs on the loan.
@@ -135,9 +142,13 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = null; this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null; this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = null; this.cumulativeFeeChargesToDate = null;
this.cumulativeChargesPaid = null; this.cumulativeFeeChargesPaid = null;
this.cumulativeChargesOutstanding = null; this.cumulativeFeeChargesOutstanding = null;
this.cumulativePenaltyChargesToDate = null;
this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesOutstanding = null;
this.totalCostOfLoan = null; this.totalCostOfLoan = null;
@@ -161,7 +172,7 @@ public class LoanScheduleData {
final BigDecimal cumulativePrincipalDue, final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalOutstanding, final BigDecimal cumulativePrincipalOutstanding,
final BigDecimal cumulativeInterestExpected, final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeChargesToDate, final BigDecimal cumulativeFeeChargesToDate,
final BigDecimal totalExpectedRepayment) { final BigDecimal totalExpectedRepayment) {
this.currency = currency; this.currency = currency;
this.periods = periods; this.periods = periods;
@@ -179,12 +190,16 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = null; this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null; this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = cumulativeChargesToDate; this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeChargesPaid = null; this.cumulativeFeeChargesPaid = null;
this.cumulativeChargesOutstanding = null; this.cumulativeFeeChargesOutstanding = null;
if (cumulativeChargesToDate != null) { this.cumulativePenaltyChargesToDate = null;
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeChargesToDate); this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesOutstanding = null;
if (cumulativeFeeChargesToDate != null) {
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate);
} else if (cumulativeInterestExpected != null) { } else if (cumulativeInterestExpected != null) {
this.totalCostOfLoan = cumulativeInterestExpected; this.totalCostOfLoan = cumulativeInterestExpected;
} else { } else {
@@ -214,9 +229,12 @@ public class LoanScheduleData {
final BigDecimal cumulativeInterestWaived, final BigDecimal cumulativeInterestWaived,
final BigDecimal cumulativeInterestWrittenOff, final BigDecimal cumulativeInterestWrittenOff,
final BigDecimal cumulativeInterestOutstanding, final BigDecimal cumulativeInterestOutstanding,
final BigDecimal cumulativeChargesToDate, final BigDecimal cumulativeFeeChargesToDate,
final BigDecimal cumulativeChargesPaid, final BigDecimal cumulativeFeeChargesPaid,
final BigDecimal cumulativeChargesOutstanding, final BigDecimal cumulativeFeeChargesOutstanding,
final BigDecimal cumulativePenaltyChargesToDate,
final BigDecimal cumulativePenaltyChargesPaid,
final BigDecimal cumulativePenaltyChargesOutstanding,
final BigDecimal totalCostOfLoan, final BigDecimal totalCostOfLoan,
final BigDecimal totalExpectedRepayment, final BigDecimal totalExpectedRepayment,
final BigDecimal totalPaidToDate, final BigDecimal totalPaidToDate,
@@ -238,9 +256,12 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff; this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
this.cumulativeInterestWaived = cumulativeInterestWaived; this.cumulativeInterestWaived = cumulativeInterestWaived;
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding; this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
this.cumulativeChargesToDate = cumulativeChargesToDate; this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeChargesPaid = cumulativeChargesPaid; this.cumulativeFeeChargesPaid = cumulativeFeeChargesPaid;
this.cumulativeChargesOutstanding = cumulativeChargesOutstanding; this.cumulativeFeeChargesOutstanding = cumulativeFeeChargesOutstanding;
this.cumulativePenaltyChargesToDate = cumulativePenaltyChargesToDate;
this.cumulativePenaltyChargesPaid = cumulativePenaltyChargesPaid;
this.cumulativePenaltyChargesOutstanding = cumulativePenaltyChargesOutstanding;
this.totalCostOfLoan = totalCostOfLoan; this.totalCostOfLoan = totalCostOfLoan;
this.totalExpectedRepayment = totalExpectedRepayment; this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = totalPaidToDate; this.totalPaidToDate = totalPaidToDate;
@@ -1,6 +1,5 @@
package org.mifosng.platform.api; package org.mifosng.platform.api;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@@ -95,25 +94,6 @@ public class LoansApiResource {
@Autowired @Autowired
private StaffReadPlatformService staffReadPlatformService; private StaffReadPlatformService staffReadPlatformService;
private final static Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "externalId", "clientId", "groupId", "clientName", "groupName", "fundId", "fundName",
"loanProductId", "loanProductName", "loanProductDescription",
"loanOfficerName", "loanOfficerId",
"currency", "principal",
"inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod",
"annualInterestRate", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType",
"interestType", "interestCalculationPeriodType",
"submittedOnDate", "approvedOnDate",
"expectedDisbursementDate", "actualDisbursementDate",
"expectedFirstRepaymentOnDate", "interestChargedFromDate",
"closedOnDate", "expectedMaturityDate",
"status",
"lifeCycleStatusDate"));
final Set<String> typicalTransactionResponseParameters = new HashSet<String>(Arrays.asList("id", "type", "date", "currency", "amount"));
@GET @GET
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@@ -124,31 +104,24 @@ public class LoansApiResource {
@QueryParam("productId") final Long productId, @QueryParam("productId") final Long productId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
responseParameters.addAll(Arrays.asList("productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
"chargeTemplate", "charges"));
// tempate related // tempate related
Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
Collection<EnumOptionData>loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); Collection<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
Collection<EnumOptionData>repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); Collection<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
Collection<EnumOptionData>interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions(); Collection<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
Collection<EnumOptionData>amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions(); Collection<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
Collection<EnumOptionData>interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions(); Collection<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
Collection<EnumOptionData>interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions(); Collection<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds(); Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies(); Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(); final boolean feeChargesOnly = false;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate(); ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
LoanBasicDetailsData loanBasicDetails; LoanBasicDetailsData loanBasicDetails;
@@ -183,11 +156,8 @@ public class LoansApiResource {
@PathParam("loanId") final Long loanId, @PathParam("loanId") final Long loanId,
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) { final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId); final LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
@@ -202,19 +172,17 @@ public class LoansApiResource {
if (!associationParameters.isEmpty()) { if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) { if (associationParameters.contains("all")) {
responseParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges")); associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges"));
} else {
responseParameters.addAll(associationParameters);
} }
if (responseParameters.contains("transactions")) { if (associationParameters.contains("transactions")) {
final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId); final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId);
if (!CollectionUtils.isEmpty(currentLoanRepayments)) { if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
loanRepayments = currentLoanRepayments; loanRepayments = currentLoanRepayments;
} }
} }
if (responseParameters.contains("repaymentSchedule")) { if (associationParameters.contains("repaymentSchedule")) {
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData(); DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(), repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(),
@@ -227,14 +195,14 @@ public class LoansApiResource {
boolean isWaiveAllowed = totalOutstandingMoney.isGreaterThanZero() && (tolerance.isGreaterThan(totalOutstandingMoney) || tolerance.isEqualTo(totalOutstandingMoney)); boolean isWaiveAllowed = totalOutstandingMoney.isGreaterThanZero() && (tolerance.isGreaterThan(totalOutstandingMoney) || tolerance.isEqualTo(totalOutstandingMoney));
if (responseParameters.contains("permissions")) { if (associationParameters.contains("permissions")) {
loanRepaymentsCount = retrieveNonDisbursementTransactions(loanRepayments); loanRepaymentsCount = retrieveNonDisbursementTransactions(loanRepayments);
permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, isWaiveAllowed, loanRepaymentsCount); permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, isWaiveAllowed, loanRepaymentsCount);
} }
convenienceDataRequired = true; convenienceDataRequired = true;
} }
if (responseParameters.contains("charges")) { if (associationParameters.contains("charges")) {
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId); charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) { if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON is no charges exist. charges = null; // set back to null so doesnt appear in JSON is no charges exist.
@@ -242,28 +210,21 @@ public class LoansApiResource {
} }
} }
Collection<LoanProductData> productOptions = new ArrayList<LoanProductData>(); Collection<LoanProductData> productOptions = null;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null; Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = new ArrayList<EnumOptionData>(); Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null; Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = new ArrayList<EnumOptionData>(); Collection<EnumOptionData> amortizationTypeOptions = null;
Collection<EnumOptionData> amortizationTypeOptions = new ArrayList<EnumOptionData>(); Collection<EnumOptionData> interestTypeOptions = null;
Collection<EnumOptionData> interestTypeOptions = new ArrayList<EnumOptionData>(); Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = new ArrayList<EnumOptionData>(); Collection<FundData> fundOptions = null;
Collection<FundData> fundOptions = new ArrayList<FundData>(); Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null; Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null; ChargeData chargeTemplate = null;
Collection<StaffData> allowedLoanOfficers = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if(template) { if(template) {
responseParameters.addAll(
Arrays.asList("productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions",
"repaymentStrategyOptions", "chargeOptions", "chargeTemplate", "loanOfficerOptions")
);
productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions(); loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions(); repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
@@ -275,7 +236,8 @@ public class LoansApiResource {
fundOptions = this.fundReadPlatformService.retrieveAllFunds(); fundOptions = this.fundReadPlatformService.retrieveAllFunds();
repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies(); repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(); final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getClientOfficeId()); allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getClientOfficeId());
chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate(); chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
} }
@@ -461,9 +423,6 @@ public class LoansApiResource {
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalTransactionResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
LoanTransactionData transactionData = null; LoanTransactionData transactionData = null;
@@ -493,9 +452,6 @@ public class LoansApiResource {
@Context final UriInfo uriInfo) { @Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalTransactionResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransactionDetails(loanId, transactionId); final LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransactionDetails(loanId, transactionId);
@@ -530,9 +486,9 @@ public class LoansApiResource {
@PathParam("loanId") final Long loanId, @PathParam("loanId") final Long loanId,
final String jsonRequestBody){ final String jsonRequestBody){
LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(null, loanId, jsonRequestBody); final LoanChargeCommand command = this.apiDataConversionService.convertJsonToLoanChargeCommand(null, loanId, jsonRequestBody);
EntityIdentifier identifier = this.loanWritePlatformService.addLoanCharge(command); final EntityIdentifier identifier = this.loanWritePlatformService.addLoanCharge(command);
return Response.ok().entity(identifier).build(); return Response.ok().entity(identifier).build();
} }
@@ -542,12 +498,14 @@ public class LoansApiResource {
@Consumes({MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveNewLoanChargeDetails(@Context final UriInfo uriInfo) { public String retrieveNewLoanChargeDetails(@Context final UriInfo uriInfo) {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(); final boolean feeChargesOnly = false;
LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions); final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions);
return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanChargeTemplate); return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanChargeTemplate);
} }
@@ -3,6 +3,9 @@ package org.mifosng.platform.api.commands;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Set; import java.util.Set;
/**
* Immutable data object for creating and modifying defined charges.
*/
public class ChargeCommand { public class ChargeCommand {
private final Long id; private final Long id;
@@ -14,12 +17,22 @@ public class ChargeCommand {
private final Integer chargeAppliesTo; private final Integer chargeAppliesTo;
private final Integer chargeCalculationType; private final Integer chargeCalculationType;
private final boolean penalty;
private final boolean active; private final boolean active;
private final Set<String> modifiedParameters; private final Set<String> modifiedParameters;
public ChargeCommand(Set<String> modifiedParameters, Long id, String name, BigDecimal amount, String currencyCode, public ChargeCommand(
Integer chargeTimeType, Integer chargeAppliesTo, Integer chargeCalculationType, boolean active) { final Set<String> modifiedParameters,
final Long id,
final String name,
final BigDecimal amount,
final String currencyCode,
final Integer chargeTimeType,
final Integer chargeAppliesTo,
final Integer chargeCalculationType,
final boolean penalty,
final boolean active) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.amount = amount; this.amount = amount;
@@ -28,6 +41,7 @@ public class ChargeCommand {
this.chargeAppliesTo = chargeAppliesTo; this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.modifiedParameters = modifiedParameters; this.modifiedParameters = modifiedParameters;
this.penalty = penalty;
this.active = active; this.active = active;
} }
@@ -62,8 +76,16 @@ public class ChargeCommand {
public Integer getChargeTimeType() { public Integer getChargeTimeType() {
return chargeTimeType; return chargeTimeType;
} }
public boolean isPenalty() {
return penalty;
}
public boolean isFee() {
return !penalty;
}
public boolean isActive() { public boolean isActive() {
return active; return active;
} }
@@ -91,7 +113,11 @@ public class ChargeCommand {
return this.modifiedParameters.contains("chargeCalculationType"); return this.modifiedParameters.contains("chargeCalculationType");
} }
public boolean isPenaltyChanged(){
return this.modifiedParameters.contains("penalty");
}
public boolean isActiveChanged(){ public boolean isActiveChanged(){
return this.modifiedParameters.contains("active"); return this.modifiedParameters.contains("active");
} }
} }
@@ -11,6 +11,7 @@ public class ChargeData {
private final Long id; private final Long id;
private final String name; private final String name;
private final boolean active; private final boolean active;
private final boolean penalty;
private final CurrencyData currency; private final CurrencyData currency;
private final BigDecimal amount; private final BigDecimal amount;
private final EnumOptionData chargeTimeType; private final EnumOptionData chargeTimeType;
@@ -27,22 +28,24 @@ public class ChargeData {
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeCalculationTypeOptions,
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeAppliesToOptions,
final List<EnumOptionData> chargeTimeTypeOptions) { final List<EnumOptionData> chargeTimeTypeOptions) {
return new ChargeData(null, null, null, null, null, null, null, false,
return new ChargeData(null, null, null, null, null, null, null, false, false,
currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions); currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions);
} }
public ChargeData(ChargeData charge, ChargeData template){ public ChargeData(final ChargeData charge, final ChargeData template){
this(charge.getId(), charge.getName(), charge.getAmount(), charge.getCurrency(), charge.getChargeTimeType(), this(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType,
charge.getChargeAppliesTo(), charge.getChargeCalculationType(), charge.isActive(), charge.chargeAppliesTo, charge.chargeCalculationType,
template.getCurrencyOptions(), template.getChargeCalculationTypeOptions(), charge.penalty, charge.active,
template.getChargeAppliesToOptions(), template.getChargeTimeTypeOptions()); template.currencyOptions, template.chargeCalculationTypeOptions,
template.chargeAppliesToOptions, template.chargeTimeTypeOptions);
} }
public ChargeData(final Long id, final String name, public ChargeData(final Long id, final String name,
final BigDecimal amount, final CurrencyData currency, final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType, final EnumOptionData chargeTimeType,
final EnumOptionData chargeAppliesTo, final EnumOptionData chargeAppliesTo,
final EnumOptionData chargeCalculationType, final boolean active) { final EnumOptionData chargeCalculationType, final boolean penalty, final boolean active) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.amount = amount; this.amount = amount;
@@ -50,6 +53,7 @@ public class ChargeData {
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
this.chargeAppliesTo = chargeAppliesTo; this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.penalty = penalty;
this.active = active; this.active = active;
this.currencyOptions = null; this.currencyOptions = null;
this.chargeCalculationTypeOptions = null; this.chargeCalculationTypeOptions = null;
@@ -61,7 +65,9 @@ public class ChargeData {
final BigDecimal amount, final CurrencyData currency, final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType, final EnumOptionData chargeTimeType,
final EnumOptionData chargeAppliesTo, final EnumOptionData chargeAppliesTo,
final EnumOptionData chargeCalculationType, final boolean active, final EnumOptionData chargeCalculationType,
final boolean penalty,
final boolean active,
final List<CurrencyData> currencyOptions, final List<CurrencyData> currencyOptions,
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeCalculationTypeOptions,
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeAppliesToOptions,
@@ -73,6 +79,7 @@ public class ChargeData {
this.chargeTimeType = chargeTimeType; this.chargeTimeType = chargeTimeType;
this.chargeAppliesTo = chargeAppliesTo; this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.penalty = penalty;
this.active = active; this.active = active;
this.currencyOptions = currencyOptions; this.currencyOptions = currencyOptions;
this.chargeCalculationTypeOptions = chargeCalculationTypeOptions; this.chargeCalculationTypeOptions = chargeCalculationTypeOptions;
@@ -88,10 +95,10 @@ public class ChargeData {
return name; return name;
} }
public boolean isActive() { public boolean isPenalty() {
return active; return this.penalty;
} }
public BigDecimal getAmount() { public BigDecimal getAmount() {
return amount; return amount;
} }
@@ -104,34 +111,14 @@ public class ChargeData {
return chargeTimeType; return chargeTimeType;
} }
public EnumOptionData getChargeAppliesTo() {
return chargeAppliesTo;
}
public EnumOptionData getChargeCalculationType() { public EnumOptionData getChargeCalculationType() {
return chargeCalculationType; return chargeCalculationType;
} }
public List<CurrencyData> getCurrencyOptions() {
return currencyOptions;
}
public List<EnumOptionData> getChargeCalculationTypeOptions() {
return chargeCalculationTypeOptions;
}
public List<EnumOptionData> getChargeAppliesToOptions() {
return chargeAppliesToOptions;
}
public List<EnumOptionData> getChargeTimeTypeOptions() {
return chargeTimeTypeOptions;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
ChargeData chargeData = (ChargeData) obj; ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.getId()); return this.id.equals(chargeData.id);
} }
@Override @Override
@@ -78,8 +78,8 @@ public class LoanBasicDetailsData {
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>(); final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
for (ChargeData charge : product.charges()) { for (ChargeData charge : product.charges()) {
charges.add(LoanChargeData.newChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(), charges.add(LoanChargeData.newLoanChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(),
charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType())); charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType(), charge.isPenalty()));
} }
return new LoanBasicDetailsData( return new LoanBasicDetailsData(
@@ -51,21 +51,25 @@ public class LoanChargeData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<ChargeData> chargeOptions; private final Collection<ChargeData> chargeOptions;
@SuppressWarnings("unused")
private final boolean penalty;
public static LoanChargeData template(final Collection<ChargeData> chargeOptions){ public static LoanChargeData template(final Collection<ChargeData> chargeOptions){
return new LoanChargeData(null,null,null,null,null,null,null, chargeOptions); return new LoanChargeData(null,null,null,null,null,null,null, chargeOptions, false);
} }
/** /**
* used when populating with details from charge definition (for crud on charges) * used when populating with details from charge definition (for crud on charges)
*/ */
public static LoanChargeData newChargeDetails( public static LoanChargeData newLoanChargeDetails(
final Long chargeId, final Long chargeId,
final String name, final String name,
final CurrencyData currency, final CurrencyData currency,
final BigDecimal value, final BigDecimal value,
final EnumOptionData chargeTimeType, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType) { final EnumOptionData chargeCalculationType,
return new LoanChargeData(null, chargeId, name, currency, value, chargeTimeType, chargeCalculationType, null); final boolean penalty) {
return new LoanChargeData(null, chargeId, name, currency, value, chargeTimeType, chargeCalculationType, null, penalty);
} }
public LoanChargeData( public LoanChargeData(
@@ -80,7 +84,8 @@ public class LoanChargeData {
final LocalDate dueAsOfDate, final LocalDate dueAsOfDate,
final EnumOptionData chargeCalculationType, final EnumOptionData chargeCalculationType,
final BigDecimal percentage, final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo) { final BigDecimal amountPercentageAppliedTo,
final boolean penalty) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -93,6 +98,7 @@ public class LoanChargeData {
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.percentage = percentage; this.percentage = percentage;
this.amountPercentageAppliedTo = amountPercentageAppliedTo; this.amountPercentageAppliedTo = amountPercentageAppliedTo;
this.penalty = penalty;
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) { if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
this.amountOrPercentage = this.percentage; this.amountOrPercentage = this.percentage;
@@ -111,7 +117,8 @@ public class LoanChargeData {
final BigDecimal amount, final BigDecimal amount,
final EnumOptionData chargeTimeType, final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType, final EnumOptionData chargeCalculationType,
final Collection<ChargeData> chargeOptions) { final Collection<ChargeData> chargeOptions,
final boolean penalty) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -124,6 +131,7 @@ public class LoanChargeData {
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.percentage = null; this.percentage = null;
this.amountPercentageAppliedTo = null; this.amountPercentageAppliedTo = null;
this.penalty = penalty;
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) { if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
this.amountOrPercentage = this.percentage; this.amountOrPercentage = this.percentage;
@@ -24,7 +24,6 @@ public class LoanSchedulePeriodData {
private final BigDecimal principalOutstanding; private final BigDecimal principalOutstanding;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal principalLoanBalanceOutstanding; private final BigDecimal principalLoanBalanceOutstanding;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal interestOriginalDue; private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue; private final BigDecimal interestDue;
@@ -32,14 +31,16 @@ public class LoanSchedulePeriodData {
private final BigDecimal interestWaived; private final BigDecimal interestWaived;
private final BigDecimal interestWrittenOff; private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding; private final BigDecimal interestOutstanding;
private final BigDecimal feeChargesDue; private final BigDecimal feeChargesDue;
private final BigDecimal feeChargesPaid; private final BigDecimal feeChargesPaid;
@SuppressWarnings("unused")
private final BigDecimal feeChargesWaived; private final BigDecimal feeChargesWaived;
@SuppressWarnings("unused")
private final BigDecimal feeChargesWrittenOff; private final BigDecimal feeChargesWrittenOff;
private final BigDecimal feeChargesOutstanding; private final BigDecimal feeChargesOutstanding;
private final BigDecimal penaltyChargesDue;
private final BigDecimal penaltyChargesPaid;
private final BigDecimal penaltyChargesWaived;
private final BigDecimal penaltyChargesWrittenOff;
private final BigDecimal penaltyChargesOutstanding;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final BigDecimal totalOriginalDueForPeriod; private final BigDecimal totalOriginalDueForPeriod;
@@ -53,15 +54,17 @@ public class LoanSchedulePeriodData {
private final BigDecimal totalWrittenOffForPeriod; private final BigDecimal totalWrittenOffForPeriod;
private final BigDecimal totalOutstandingForPeriod; private final BigDecimal totalOutstandingForPeriod;
private final BigDecimal totalOverdue; private final BigDecimal totalOverdue;
@SuppressWarnings("unused")
private final BigDecimal totalActualCostOfLoanForPeriod;
public static LoanSchedulePeriodData disbursementOnlyPeriod( public static LoanSchedulePeriodData disbursementOnlyPeriod(
final LocalDate disbursementDate, final LocalDate disbursementDate,
final BigDecimal principalDisbursed, final BigDecimal principalDisbursed,
final BigDecimal chargesDueAtTimeOfDisbursement, final BigDecimal feeChargesDueAtTimeOfDisbursement,
final boolean isDisbursed) { final boolean isDisbursed) {
Integer periodNumber = Integer.valueOf(0); Integer periodNumber = Integer.valueOf(0);
LocalDate from = null; LocalDate from = null;
return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, chargesDueAtTimeOfDisbursement, isDisbursed); return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, feeChargesDueAtTimeOfDisbursement, isDisbursed);
} }
public static LoanSchedulePeriodData repaymentOnlyPeriod( public static LoanSchedulePeriodData repaymentOnlyPeriod(
@@ -71,10 +74,12 @@ public class LoanSchedulePeriodData {
final BigDecimal principalDue, final BigDecimal principalDue,
final BigDecimal principalOutstanding, final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal chargesDueForPeriod, final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
final BigDecimal totalDueForPeriod) { final BigDecimal totalDueForPeriod) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding, interestDueOnPrincipalOutstanding, chargesDueForPeriod, totalDueForPeriod); return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, principalDue, principalOutstanding,
interestDueOnPrincipalOutstanding, feeChargesDueForPeriod, penaltyChargesDueForPeriod, totalDueForPeriod);
} }
public static LoanSchedulePeriodData repaymentPeriodWithPayments( public static LoanSchedulePeriodData repaymentPeriodWithPayments(
@@ -97,17 +102,24 @@ public class LoanSchedulePeriodData {
final BigDecimal feeChargesWaived, final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding, final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod, final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid, final BigDecimal totalPaid,
final BigDecimal totalWaived, final BigDecimal totalWaived,
final BigDecimal totalWrittenOff, final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) { final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate, return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan, principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding); penaltyChargesDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding,
totalDueForPeriod, totalPaid, totalWaived, totalWrittenOff, totalOutstanding, totalActualCostOfLoanForPeriod);
} }
/* /*
@@ -146,22 +158,29 @@ public class LoanSchedulePeriodData {
this.feeChargesDue = chargesDueAtTimeOfDisbursement; this.feeChargesDue = chargesDueAtTimeOfDisbursement;
if (isDisbursed) { if (isDisbursed) {
this.feeChargesPaid = chargesDueAtTimeOfDisbursement; this.feeChargesPaid = chargesDueAtTimeOfDisbursement;
this.feeChargesWaived = BigDecimal.ZERO; this.feeChargesWaived = null;
this.feeChargesWrittenOff = BigDecimal.ZERO; this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = BigDecimal.ZERO; this.feeChargesOutstanding = null;
} else { } else {
this.feeChargesPaid = BigDecimal.ZERO; this.feeChargesPaid = null;
this.feeChargesWaived = BigDecimal.ZERO; this.feeChargesWaived = null;
this.feeChargesWrittenOff = BigDecimal.ZERO; this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement; this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement;
} }
this.penaltyChargesDue = null;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement; this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement; this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = this.feeChargesPaid; this.totalPaidForPeriod = this.feeChargesPaid;
this.totalWaivedForPeriod = null; this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null; this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = this.feeChargesOutstanding; this.totalOutstandingForPeriod = this.feeChargesOutstanding;
this.totalActualCostOfLoanForPeriod = this.feeChargesDue;
if (dueDate.isBefore(new LocalDate())) { if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod; this.totalOverdue = this.totalOutstandingForPeriod;
} else { } else {
@@ -176,7 +195,8 @@ public class LoanSchedulePeriodData {
final BigDecimal principalOriginalDue, final BigDecimal principalOriginalDue,
final BigDecimal principalOutstanding, final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding, final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal chargesDueForPeriod, final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
final BigDecimal totalDueForPeriod) { final BigDecimal totalDueForPeriod) {
this.period = periodNumber; this.period = periodNumber;
this.fromDate = fromDate; this.fromDate = fromDate;
@@ -201,18 +221,26 @@ public class LoanSchedulePeriodData {
this.interestWrittenOff = null; this.interestWrittenOff = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding; this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.feeChargesDue = chargesDueForPeriod; this.feeChargesDue = feeChargesDueForPeriod;
this.feeChargesPaid = null; this.feeChargesPaid = null;
this.feeChargesWaived = null; this.feeChargesWaived = null;
this.feeChargesWrittenOff = null; this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null; this.feeChargesOutstanding = null;
this.penaltyChargesDue = penaltyChargesDueForPeriod;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = totalDueForPeriod; this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod; this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO; this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null; this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null; this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod; this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod);
if (dueDate.isBefore(new LocalDate())) { if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod; this.totalOverdue = this.totalOutstandingForPeriod;
} else { } else {
@@ -242,11 +270,17 @@ public class LoanSchedulePeriodData {
final BigDecimal feeChargesWaived, final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff, final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding, final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod, final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid, final BigDecimal totalPaid,
final BigDecimal totalWaived, final BigDecimal totalWaived,
final BigDecimal totalWrittenOff, final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) { final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
this.period = periodNumber; this.period = periodNumber;
this.fromDate = fromDate; this.fromDate = fromDate;
this.dueDate = dueDate; this.dueDate = dueDate;
@@ -276,12 +310,19 @@ public class LoanSchedulePeriodData {
this.feeChargesWrittenOff = feeChargesWrittenOff; this.feeChargesWrittenOff = feeChargesWrittenOff;
this.feeChargesOutstanding = feeChargesOutstanding; this.feeChargesOutstanding = feeChargesOutstanding;
this.penaltyChargesDue = penaltyChargesDue;
this.penaltyChargesPaid = penaltyChargesPaid;
this.penaltyChargesWaived = penaltyChargesWaived;
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
this.penaltyChargesOutstanding = penaltyChargesOutstanding;
this.totalOriginalDueForPeriod = totalDueForPeriod; this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod; this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = totalPaid; this.totalPaidForPeriod = totalPaid;
this.totalWaivedForPeriod = totalWaived; this.totalWaivedForPeriod = totalWaived;
this.totalWrittenOffForPeriod = totalWrittenOff; this.totalWrittenOffForPeriod = totalWrittenOff;
this.totalOutstandingForPeriod = totalOutstanding; this.totalOutstandingForPeriod = totalOutstanding;
this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod;
if (dueDate.isBefore(new LocalDate())) { if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod; this.totalOverdue = this.totalOutstandingForPeriod;
@@ -310,6 +351,10 @@ public class LoanSchedulePeriodData {
return this.period; return this.period;
} }
public LocalDate periodFromDate() {
return this.fromDate;
}
public LocalDate periodDueDate() { public LocalDate periodDueDate() {
return this.dueDate; return this.dueDate;
} }
@@ -358,17 +403,45 @@ public class LoanSchedulePeriodData {
return defaultToZeroIfNull(this.interestOutstanding); return defaultToZeroIfNull(this.interestOutstanding);
} }
public BigDecimal chargesDue() { public BigDecimal feeChargesDue() {
return defaultToZeroIfNull(this.feeChargesDue); return defaultToZeroIfNull(this.feeChargesDue);
} }
public BigDecimal feeChargesWaived() {
return defaultToZeroIfNull(this.feeChargesWaived);
}
public BigDecimal feeChargesWrittenOff() {
return defaultToZeroIfNull(this.feeChargesWrittenOff);
}
public BigDecimal chargesPaid() { public BigDecimal feeChargesPaid() {
return defaultToZeroIfNull(this.feeChargesPaid); return defaultToZeroIfNull(this.feeChargesPaid);
} }
public BigDecimal chargesOutstanding() { public BigDecimal feeChargesOutstanding() {
return defaultToZeroIfNull(this.feeChargesOutstanding); return defaultToZeroIfNull(this.feeChargesOutstanding);
} }
public BigDecimal penaltyChargesDue() {
return defaultToZeroIfNull(this.penaltyChargesDue);
}
public BigDecimal penaltyChargesWaived() {
return defaultToZeroIfNull(this.penaltyChargesWaived);
}
public BigDecimal penaltyChargesWrittenOff() {
return defaultToZeroIfNull(this.penaltyChargesWrittenOff);
}
public BigDecimal penaltyChargesPaid() {
return defaultToZeroIfNull(this.penaltyChargesPaid);
}
public BigDecimal penaltyChargesOutstanding() {
return defaultToZeroIfNull(this.penaltyChargesOutstanding);
}
public BigDecimal totalOverdue() { public BigDecimal totalOverdue() {
return defaultToZeroIfNull(this.totalOverdue); return defaultToZeroIfNull(this.totalOverdue);
@@ -72,31 +72,32 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
throw new InvalidJsonException(); throw new InvalidJsonException();
} }
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType(); final Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap); final Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(
Set<String> supportedParams = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode", Arrays.asList("name", "amount", "locale", "currencyCode",
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "currencyOptions", "chargeAppliesTo", "chargeTimeType",
"chargeCalculationType", "chargeCalculationTypeOptions", "active" "chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active"
)); ));
checkForUnsupportedParameters(requestMap, supportedParams); checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>(); Set<String> modifiedParameters = new HashSet<String>();
String name = extractStringParameter("name", requestMap, modifiedParameters); final String name = extractStringParameter("name", requestMap, modifiedParameters);
BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters); final BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters);
String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters); final String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters);
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters); final Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters);
Integer chargeAppliesTo = extractIntegerParameter("chargeAppliesTo", requestMap, modifiedParameters); final Integer chargeAppliesTo = extractIntegerParameter("chargeAppliesTo", requestMap, modifiedParameters);
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", requestMap, modifiedParameters); final Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", requestMap, modifiedParameters);
boolean active = extractBooleanParameter("active", requestMap, modifiedParameters); final boolean penalty = extractBooleanParameter("penalty", requestMap, modifiedParameters);
final boolean active = extractBooleanParameter("active", requestMap, modifiedParameters);
return new ChargeCommand(modifiedParameters, resourceIdentifier, name, amount, return new ChargeCommand(modifiedParameters, resourceIdentifier, name, amount,
currencyCode, chargeTimeType, chargeAppliesTo, chargeCalculationType, active); currencyCode, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active);
} }
@Override @Override
@@ -188,7 +188,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
Arrays.asList("id", "type", "date", "currency", "amount")); Arrays.asList("id", "type", "date", "currency", "amount"));
private static final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<String>( private static final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "name", "amount", "currency", "active", Arrays.asList("id", "name", "amount", "currency", "penalty", "active",
"chargeAppliesTo", "chargeTimeType", "chargeAppliesTo", "chargeTimeType",
"chargeCalculationType", "chargeCalculationTypeOptions", "chargeCalculationType", "chargeCalculationTypeOptions",
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions")); "chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions"));
@@ -38,7 +38,7 @@ public class GoogleGsonSerializerHelper {
if (!responseParameters.isEmpty()) { if (!responseParameters.isEmpty()) {
// strip out all know support parameters from expected response to // strip out all known support parameters from expected response to
// see if unsupported parameters requested for response. // see if unsupported parameters requested for response.
final Set<String> differentParametersDetectedSet = new HashSet<String>(responseParameters); final Set<String> differentParametersDetectedSet = new HashSet<String>(responseParameters);
differentParametersDetectedSet.removeAll(supportedParameters); differentParametersDetectedSet.removeAll(supportedParameters);
@@ -32,15 +32,23 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "charge_calculation_enum") @Column(name = "charge_calculation_enum")
private Integer chargeCalculation; private Integer chargeCalculation;
@Column(name = "is_penalty", nullable = false)
private boolean penalty;
@Column(name = "is_active", nullable = false) @Column(name = "is_active", nullable = false)
private boolean active; private boolean active;
@Column(name = "is_deleted", nullable=false) @Column(name = "is_deleted", nullable=false)
private boolean deleted = false; private boolean deleted = false;
public static Charge createNew(final String name, final BigDecimal amount, final String currencyCode, public static Charge createNew(
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean active) { final String name, final BigDecimal amount, final String currencyCode,
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTime, chargeCalculationType, active); final ChargeAppliesTo chargeAppliesTo,
final ChargeTimeType chargeTime,
final ChargeCalculationType chargeCalculationType,
final boolean penalty,
final boolean active) {
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTime, chargeCalculationType, penalty, active);
} }
protected Charge() { protected Charge() {
@@ -49,13 +57,14 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
private Charge(final String name, final BigDecimal amount, final String currencyCode, private Charge(final String name, final BigDecimal amount, final String currencyCode,
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime, final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime,
final ChargeCalculationType chargeCalculationType, final boolean active) { final ChargeCalculationType chargeCalculationType, final boolean penalty, boolean active) {
this.name = name; this.name = name;
this.amount = amount; this.amount = amount;
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
this.chargeAppliesTo = chargeAppliesTo.getValue(); this.chargeAppliesTo = chargeAppliesTo.getValue();
this.chargeTime = chargeTime.getValue(); this.chargeTime = chargeTime.getValue();
this.chargeCalculation = chargeCalculationType.getValue(); this.chargeCalculation = chargeCalculationType.getValue();
this.penalty = penalty;
this.active = active; this.active = active;
} }
@@ -82,6 +91,10 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
public boolean isActive() { public boolean isActive() {
return active; return active;
} }
public boolean isPenalty() {
return penalty;
}
public boolean isDeleted() { public boolean isDeleted() {
return deleted; return deleted;
@@ -126,4 +139,4 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
this.deleted = true; this.deleted = true;
this.name = this.getId() + "_" + this.name; this.name = this.getId() + "_" + this.name;
} }
} }
@@ -27,6 +27,10 @@ public class ChargeCommandValidator {
baseDataValidator.reset().parameter("chargeAppliesTo").value(command.getChargeAppliesTo()).notNull().inMinMaxRange(1, 1); baseDataValidator.reset().parameter("chargeAppliesTo").value(command.getChargeAppliesTo()).notNull().inMinMaxRange(1, 1);
baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).notNull().inMinMaxRange(1, 2); baseDataValidator.reset().parameter("chargeTimeType").value(command.getChargeTimeType()).notNull().inMinMaxRange(1, 2);
baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).notNull().inMinMaxRange(1, 4); baseDataValidator.reset().parameter("chargeCalculationType").value(command.getChargeCalculationType()).notNull().inMinMaxRange(1, 4);
if (command.isPenalty() && command.getChargeTimeType().equals(Integer.valueOf(1))) {
// FIXME - KW - cannot have penalty charge at time of disbursement
}
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
@@ -13,7 +13,7 @@ public interface ChargeReadPlatformService {
ChargeData retrieveNewChargeDetails(); ChargeData retrieveNewChargeDetails();
Collection<ChargeData> retrieveLoanApplicableCharges(); Collection<ChargeData> retrieveLoanApplicableCharges(boolean feeChargesOnly);
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId); Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId);
@@ -101,18 +101,22 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final LoanChargeMapper rm = new LoanChargeMapper(); final LoanChargeMapper rm = new LoanChargeMapper();
final String sql = "select " + rm.loanChargeSchema() + " where c.is_deleted=0 and lc.loan_id=?"; final String sql = "select " + rm.loanChargeSchema() + " where c.is_deleted=0 and lc.loan_id=? " +
" order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] {loanId}); return this.jdbcTemplate.query(sql, rm, new Object[] {loanId});
} }
@Override @Override
public Collection<ChargeData> retrieveLoanApplicableCharges() { public Collection<ChargeData> retrieveLoanApplicableCharges(final boolean feeChargesOnly) {
this.context.authenticatedUser(); this.context.authenticatedUser();
final ChargeMapper rm = new ChargeMapper(); final ChargeMapper rm = new ChargeMapper();
final String sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=? order by c.name "; String sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 and c.is_active=1 and c.charge_applies_to_enum=? order by c.name ";
if (feeChargesOnly) {
sql = "select " + rm.chargeSchema() + " where c.is_deleted=0 and c.is_active=1 and c.is_penalty=0 and c.charge_applies_to_enum=? order by c.name ";
}
return this.jdbcTemplate.query(sql, rm, new Object[] {ChargeAppliesTo.LOAN.getValue()}); return this.jdbcTemplate.query(sql, rm, new Object[] {ChargeAppliesTo.LOAN.getValue()});
} }
@@ -132,7 +136,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
public String chargeSchema(){ public String chargeSchema(){
return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, " + return "c.id as id, c.name as name, c.amount as amount, c.currency_code as currencyCode, " +
"c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, " + "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, " +
"c.charge_calculation_enum as chargeCalculation, c.is_active as active, oc.name as currencyName, " + "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, c.is_active as active, oc.name as currencyName, " +
"oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " + "oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, " +
"oc.internationalized_name_code as currencyNameCode from m_charge c " + "oc.internationalized_name_code as currencyNameCode from m_charge c " +
"join m_organisation_currency oc on c.currency_code = oc.code"; "join m_organisation_currency oc on c.currency_code = oc.code";
@@ -165,9 +169,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final int chargeCalculation = rs.getInt("chargeCalculation"); final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation); final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
final boolean active = rs.getBoolean("active"); final boolean active = rs.getBoolean("active");
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, active); return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, penalty, active);
} }
} }
@@ -178,6 +183,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
"lc.amount as amountDue, lc.amount_paid_derived as amountPaid, lc.amount_outstanding_derived as amountOutstanding, " + "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.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " +
"lc.charge_time_enum as chargeTime, " + "lc.charge_time_enum as chargeTime, " +
"lc.is_penalty as penalty, " +
"lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.due_for_collection_as_of_date as dueAsOfDate, " +
"lc.charge_calculation_enum as chargeCalculation, " + "lc.charge_calculation_enum as chargeCalculation, " +
"c.currency_code as currencyCode, oc.name as currencyName, " + "c.currency_code as currencyCode, oc.name as currencyName, " +
@@ -194,14 +200,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final Long chargeId = rs.getLong("chargeId"); final Long chargeId = rs.getLong("chargeId");
final String name = rs.getString("name"); final String name = rs.getString("name");
final BigDecimal amount = rs.getBigDecimal("amountDue"); final BigDecimal amount = rs.getBigDecimal("amountDue");
BigDecimal amountPaid = rs.getBigDecimal("amountPaid"); final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
if (amountPaid == null) {
amountPaid = BigDecimal.ZERO;
}
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding"); final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal percentageOf = rs.getBigDecimal("percentageOf"); final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
final BigDecimal amountPercentageAppliedTo = rs.getBigDecimal("amountPercentageAppliedTo"); final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
final String currencyCode = rs.getString("currencyCode"); final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName"); final String currencyName = rs.getString("currencyName");
@@ -219,8 +222,9 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final int chargeCalculation = rs.getInt("chargeCalculation"); final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation); final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo);
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty);
} }
} }
} }
@@ -39,7 +39,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
final ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command.getChargeCalculationType()); final ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command.getChargeCalculationType());
final 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()); chargeAppliesTo, chargeTimeType, chargeCalculationType, command.isPenalty(), command.isActive());
this.chargeRepository.saveAndFlush(charge); this.chargeRepository.saveAndFlush(charge);
@@ -378,11 +378,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (scheduledLoanInstallment.isRepaymentPeriod()) { if (scheduledLoanInstallment.isRepaymentPeriod()) {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment( LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
this, scheduledLoanInstallment.periodNumber(), this,
scheduledLoanInstallment.periodNumber(),
scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.periodDueDate(),
scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.interestDue(),
scheduledLoanInstallment.chargesDue()); scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue());
this.addRepaymentScheduleInstallment(installment); this.addRepaymentScheduleInstallment(installment);
} }
} }
@@ -60,6 +60,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false) @Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal amountOutstanding; private BigDecimal amountOutstanding;
@Column(name = "is_penalty", nullable=false)
private boolean penaltyCharge = false;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Column(name = "is_paid_derived", nullable=false) @Column(name = "is_paid_derived", nullable=false)
@@ -83,11 +86,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
protected LoanCharge() { protected LoanCharge() {
// //
} }
public LoanCharge(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) { public LoanCharge(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) {
this.loan = null; this.loan = null;
this.charge = chargeDefinition; this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty();
if (command.isChargeTimeTypeChanged()) { if (command.isChargeTimeTypeChanged()) {
this.chargeTime = command.getChargeTimeType(); this.chargeTime = command.getChargeTimeType();
@@ -125,7 +128,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) { private LoanCharge(final Loan loan, final Charge chargeDefinition, final LoanChargeCommand command) {
this.loan = loan; this.loan = loan;
this.charge = chargeDefinition; this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty();
if (command.isChargeTimeTypeChanged()){ if (command.isChargeTimeTypeChanged()){
this.chargeTime = command.getChargeTimeType(); this.chargeTime = command.getChargeTimeType();
} else { } else {
@@ -344,4 +347,12 @@ public class LoanCharge extends AbstractPersistable<Long> {
final LocalDate specifiedDueDate) { final LocalDate specifiedDueDate) {
return specifiedDueDate != null && fromNotInclusive.isBefore(specifiedDueDate) && (toInclusive.isAfter(specifiedDueDate) || toInclusive.isEqual(specifiedDueDate)); return specifiedDueDate != null && fromNotInclusive.isBefore(specifiedDueDate) && (toInclusive.isAfter(specifiedDueDate) || toInclusive.isEqual(specifiedDueDate));
} }
public boolean isFeeCharge() {
return !this.penaltyCharge;
}
public boolean isPenaltyCharge() {
return this.penaltyCharge;
}
} }
@@ -25,85 +25,89 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
@JoinColumn(name = "loan_id") @JoinColumn(name = "loan_id")
private Loan loan; private Loan loan;
@Column(name = "installment") @Column(name = "installment", nullable=false)
private final Integer installmentNumber; private final Integer installmentNumber;
@Column(name = "principal_amount", scale=6, precision=19) @Temporal(TemporalType.DATE)
@Column(name = "fromdate", nullable=true)
private final Date fromDate;
@Temporal(TemporalType.DATE)
@Column(name = "duedate", nullable=false)
private final Date dueDate;
@Column(name = "principal_amount", scale=6, precision=19, nullable=true)
private BigDecimal principal; private BigDecimal principal;
@Column(name = "interest_amount", scale=6, precision=19) @Column(name = "principal_completed_derived", scale=6, precision=19, nullable=true)
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; private BigDecimal principalCompleted;
@Column(name = "principal_writtenoff_derived", scale=6, precision=19) @Column(name = "principal_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal principalWrittenOff; private BigDecimal principalWrittenOff;
@Column(name = "interest_completed_derived", scale = 6, precision = 19) @Column(name = "interest_amount", scale=6, precision=19, nullable=true)
private BigDecimal interest;
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable=true)
private BigDecimal interestCompleted; private BigDecimal interestCompleted;
@Column(name = "interest_waived_derived", scale = 6, precision = 19) @Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable=true)
private BigDecimal interestWaived; private BigDecimal interestWaived;
@Column(name = "interest_writtenoff_derived", scale=6, precision=19) @Column(name = "interest_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal interestWrittenOff; private BigDecimal interestWrittenOff;
@Column(name = "fee_charges_amount", scale=6, precision=19, nullable=true)
private BigDecimal feeCharges;
@Column(name="completed_derived") @Column(name = "fee_charges_completed_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesCompleted;
@Column(name = "fee_charges_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesWrittenOff;
@Column(name = "fee_charges_waived_derived", scale=6, precision=19, nullable=true)
private BigDecimal feeChargesWaived;
@Column(name = "penalty_charges_amount", scale=6, precision=19, nullable=true)
private BigDecimal penaltyCharges;
@Column(name = "penalty_charges_completed_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesCompleted;
@Column(name = "penalty_charges_writtenoff_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesWrittenOff;
@Column(name = "penalty_charges_waived_derived", scale=6, precision=19, nullable=true)
private BigDecimal penaltyChargesWaived;
@Column(name="completed_derived", nullable=false)
private boolean completed; private boolean completed;
@Temporal(TemporalType.DATE)
@Column(name = "duedate")
private final Date dueDate;
protected LoanRepaymentScheduleInstallment() { protected LoanRepaymentScheduleInstallment() {
this.loan = null; this.installmentNumber = null;
this.installmentNumber = null; this.fromDate = null;
this.dueDate = null; this.dueDate = null;
this.principal = null;
this.interest = null;
this.principalCompleted = null;
this.interestCompleted = null;
this.completed = false; this.completed = false;
} }
public LoanRepaymentScheduleInstallment( public LoanRepaymentScheduleInstallment(
final Loan loan, final Loan loan,
final Integer installmentNumber, final Integer installmentNumber,
final LocalDate fromDate,
final LocalDate dueDate, final LocalDate dueDate,
final BigDecimal principal, final BigDecimal principal,
final BigDecimal interest, final BigDecimal interest,
final BigDecimal feeCharges) { final BigDecimal feeCharges,
final BigDecimal penaltyCharges) {
this.loan = loan; this.loan = loan;
this.installmentNumber = installmentNumber; this.installmentNumber = installmentNumber;
this.fromDate = fromDate.toDateMidnight().toDate();
this.dueDate = dueDate.toDateMidnight().toDate(); this.dueDate = dueDate.toDateMidnight().toDate();
this.principal = defaultToNullIfZero(principal); this.principal = defaultToNullIfZero(principal);
this.interest = defaultToNullIfZero(interest); this.interest = defaultToNullIfZero(interest);
this.feeCharges = defaultToNullIfZero(feeCharges); this.feeCharges = defaultToNullIfZero(feeCharges);
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
this.completed = false; this.completed = false;
} }
@@ -219,11 +223,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
} }
public void resetDerivedComponents() { public void resetDerivedComponents() {
this.principalCompleted = BigDecimal.ZERO; this.principalCompleted = null;
this.principalWrittenOff = BigDecimal.ZERO; this.principalWrittenOff = null;
this.interestCompleted = BigDecimal.ZERO; this.interestCompleted = null;
this.interestWaived = BigDecimal.ZERO; this.interestWaived = null;
this.interestWrittenOff = BigDecimal.ZERO; this.interestWrittenOff = null;
this.completed = false; this.completed = false;
} }
@@ -241,6 +245,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining); interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining);
} }
this.interestCompleted = defaultToNullIfZero(this.interestCompleted);
this.completed = getTotalOutstanding(currency).isZero(); this.completed = getTotalOutstanding(currency).isZero();
return interestPortionOfTransaction; return interestPortionOfTransaction;
@@ -260,6 +266,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
principalPortionOfTransaction = principalPortionOfTransaction.plus(transactionAmountRemaining); principalPortionOfTransaction = principalPortionOfTransaction.plus(transactionAmountRemaining);
} }
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
this.completed = getTotalOutstanding(currency).isZero(); this.completed = getTotalOutstanding(currency).isZero();
return principalPortionOfTransaction; return principalPortionOfTransaction;
@@ -278,6 +286,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(transactionAmountRemaining); waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(transactionAmountRemaining);
} }
this.interestWaived = defaultToNullIfZero(this.interestWaived);
this.completed = getTotalOutstanding(currency).isZero(); this.completed = getTotalOutstanding(currency).isZero();
return waivedInterestPortionOfTransaction; return waivedInterestPortionOfTransaction;
@@ -286,7 +296,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) { public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) {
final Money principalDue = getPrincipalOutstanding(currency); final Money principalDue = getPrincipalOutstanding(currency);
this.principalWrittenOff = principalDue.getAmount(); this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero(); this.completed = getTotalOutstanding(currency).isZero();
return principalDue; return principalDue;
@@ -295,7 +305,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public Money writeOffOutstandingInterest(final MonetaryCurrency currency) { public Money writeOffOutstandingInterest(final MonetaryCurrency currency) {
final Money interestDue = getInterestOutstanding(currency); final Money interestDue = getInterestOutstanding(currency);
this.interestWrittenOff = interestDue.getAmount(); this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero(); this.completed = getTotalOutstanding(currency).isZero();
return interestDue; return interestDue;
@@ -305,7 +315,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
return this.getDueDate().isBefore(transactionDate); return this.getDueDate().isBefore(transactionDate);
} }
public void updateChargePortion(final Money feeChargesDue) { public void updateChargePortion(final Money feeChargesDue, final Money penaltyChargesDue) {
this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount()); this.feeCharges = defaultToNullIfZero(feeChargesDue.getAmount());
this.penaltyCharges = defaultToNullIfZero(penaltyChargesDue.getAmount());
} }
} }
@@ -29,7 +29,6 @@ public class LoanScheduleWrapper {
/** /**
* 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. * 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( public void reprocess(
final MonetaryCurrency currency, final MonetaryCurrency currency,
@@ -42,15 +41,15 @@ public class LoanScheduleWrapper {
for (LoanRepaymentScheduleInstallment period : repaymentPeriods) { 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. // 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); final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
// if (feeChargesDueForRepaymentPeriod.isGreaterThanZero()) { final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
period.updateChargePortion(feeChargesDueForRepaymentPeriod); period.updateChargePortion(feeChargesDueForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod);
// }
startDate = period.getDueDate(); startDate = period.getDueDate();
} }
} }
private Money cumulativeChargesDueWithin( private Money cumulativeFeeChargesDueWithin(
final LocalDate periodStart, final LocalDate periodStart,
final LocalDate periodEnd, final LocalDate periodEnd,
final Set<LoanCharge> loanCharges, final Set<LoanCharge> loanCharges,
@@ -59,7 +58,24 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(monetaryCurrency); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd)) { if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
return cumulative;
}
private Money cumulativePenaltyChargesDueWithin(
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) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -137,11 +137,14 @@ public class LoanAssembler {
if (scheduledLoanInstallment.isRepaymentPeriod()) { if (scheduledLoanInstallment.isRepaymentPeriod()) {
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment( final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanInstallment.periodNumber(), loan,
scheduledLoanInstallment.periodNumber(),
scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.periodDueDate(),
scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.interestDue(),
scheduledLoanInstallment.chargesDue()); scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue());
loan.addRepaymentScheduleInstallment(installment); loan.addRepaymentScheduleInstallment(installment);
} }
@@ -98,7 +98,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId, public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
final CurrencyData currency, final CurrencyData currency,
final DisbursementData disbursement, final DisbursementData disbursement,
final BigDecimal totalChargesAtDisbursement, final BigDecimal totalFeeChargesAtDisbursement,
final BigDecimal inArrearsTolerance) { final BigDecimal inArrearsTolerance) {
try { try {
@@ -107,7 +107,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement); final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement);
final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment"; final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment";
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), totalChargesAtDisbursement, disbursement.isDisbursed()); final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), totalFeeChargesAtDisbursement, disbursement.isDisbursed());
final Collection<LoanSchedulePeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); final Collection<LoanSchedulePeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
@@ -128,16 +128,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived(); final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived();
final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff(); final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff();
final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding(); final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding();
final BigDecimal cumulativeChargesToDate = wrapper.deriveCumulativeChargesToDate(); final BigDecimal cumulativeFeeChargesToDate = wrapper.deriveCumulativeFeeChargesToDate();
final BigDecimal cumulativeChargesPaid = wrapper.deriveCumulativeChargesPaid(); final BigDecimal cumulativeFeeChargesPaid = wrapper.deriveCumulativeFeeChargesPaid();
final BigDecimal cumulativeChargesOutstanding = wrapper.deriveCumulativeChargesOutstanding(); final BigDecimal cumulativeFeeChargesOutstanding = wrapper.deriveCumulativeFeeChargesOutstanding();
final BigDecimal cumulativePenaltyChargesToDate = wrapper.deriveCumulativePenaltyChargesToDate();
final BigDecimal cumulativePenaltyChargesPaid = wrapper.deriveCumulativePenaltyChargesPaid();
final BigDecimal cumulativePenaltyChargesOutstanding = wrapper.deriveCumulativePenaltyChargesOutstanding();
final BigDecimal totalCostOfLoan = cumulativeInterestExpected.add(cumulativeChargesToDate); final BigDecimal totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate).add(cumulativePenaltyChargesToDate);
final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalCostOfLoan); final BigDecimal totalExpectedRepayment = cumulativePrincipalDisbursed.add(totalCostOfLoan);
final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeChargesPaid); final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeFeeChargesPaid);
final BigDecimal totalWaivedToDate = cumulativeInterestWaived; final BigDecimal totalWaivedToDate = cumulativeInterestWaived;
final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff); final BigDecimal totalWrittenOffToDate = cumulativePrincipalWrittenOff.add(cumulativeInterestWrittenOff);
final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding).add(cumulativeChargesOutstanding); final BigDecimal totalOutstanding = cumulativePrincipalOutstanding.add(cumulativeInterestOutstanding).add(cumulativeFeeChargesOutstanding);
final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue(); final BigDecimal totalOverdue = wrapper.deriveCumulativeTotalOverdue();
@@ -154,7 +157,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return new LoanScheduleData(currency, periods, loanTermInDays, return new LoanScheduleData(currency, periods, loanTermInDays,
cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding, cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding,
cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding, cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding,
cumulativeChargesToDate, cumulativeChargesPaid, cumulativeChargesOutstanding, cumulativeFeeChargesToDate, cumulativeFeeChargesPaid, cumulativeFeeChargesOutstanding,
cumulativePenaltyChargesToDate, cumulativePenaltyChargesPaid, cumulativePenaltyChargesOutstanding,
totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, totalOutstanding, totalInArrears); totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, totalOutstanding, totalInArrears);
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId); throw new LoanNotFoundException(loanId);
@@ -516,10 +520,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public String loanScheduleSchema() { public String loanScheduleSchema() {
return " ls.loan_id as loanId, ls.installment as period, ls.duedate as dueDate, " return " ls.loan_id as loanId, ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, "
+ " ls.principal_amount as principalDue, ls.principal_completed_derived as principalPaid, ls.principal_writtenoff_derived as principalWrittenOff, " + " ls.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 " + " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
+ " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff "
+ " from m_loan l " + " from m_loan l "
+ " join m_loan_repayment_schedule ls on ls.loan_id = l.id "; + " join m_loan_repayment_schedule ls on ls.loan_id = l.id ";
} }
@@ -529,6 +534,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Long loanId = rs.getLong("loanId"); final Long loanId = rs.getLong("loanId");
final Integer period = JdbcSupport.getInteger(rs, "period"); final Integer period = JdbcSupport.getInteger(rs, "period");
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate"); final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue"); final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid"); final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
@@ -537,28 +543,43 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
// TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns? // 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 principalOutstanding = principalDue.subtract(principalPaid).subtract(principalWrittenOff);
final BigDecimal interestDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue"); final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid"); final BigDecimal interestPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestPaid");
final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived"); final BigDecimal interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff"); 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? // 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 interestActualDue = interestExpectedDue.subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal interestOutstanding = interestActualDue.subtract(interestPaid);
final BigDecimal feeChargesDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue"); final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue");
final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid"); final BigDecimal feeChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesPaid");
final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived"); final BigDecimal feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff"); final BigDecimal feeChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWrittenOff");
final BigDecimal feeChargesOutstanding = feeChargesDue.subtract(feeChargesPaid).subtract(feeChargesWaived).subtract(feeChargesWrittenOff); final BigDecimal feeChargesActualDue = feeChargesExpectedDue.subtract(feeChargesWaived).subtract(feeChargesWrittenOff);
final BigDecimal feeChargesOutstanding = feeChargesActualDue.subtract(feeChargesPaid);
final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue");
final BigDecimal penaltyChargesPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesPaid");
final BigDecimal penaltyChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWaived");
final BigDecimal penaltyChargesWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesWrittenOff");
final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract(penaltyChargesWrittenOff);
final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid);
final BigDecimal totalDueForPeriod = principalDue.add(interestDue).add(feeChargesDue); final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(penaltyChargesExpectedDue);
final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod);
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid); final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid);
final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived); final BigDecimal totalWaivedForPeriod = interestWaived.add(feeChargesWaived);
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff); final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff);
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding); final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding);
final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue);
final LocalDate fromDate = this.lastDueDate;
if (fromDate == null) {
fromDate = this.lastDueDate;
}
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue); final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
// update based on current period values // update based on current period values
@@ -567,9 +588,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate,
principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan, principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan,
interestDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, feeChargesExpectedDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod); penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding,
totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod);
} }
} }
@@ -93,26 +93,50 @@ public class LoanSchedulePeriodDataWrapper {
return cumulativeValue; return cumulativeValue;
} }
public BigDecimal deriveCumulativeChargesToDate() { public BigDecimal deriveCumulativeFeeChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO; BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) { for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesDue()); cumulativeValue = cumulativeValue.add(period.feeChargesDue());
} }
return cumulativeValue; return cumulativeValue;
} }
public BigDecimal deriveCumulativeChargesPaid() { public BigDecimal deriveCumulativeFeeChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO; BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) { for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesPaid()); cumulativeValue = cumulativeValue.add(period.feeChargesPaid());
} }
return cumulativeValue; return cumulativeValue;
} }
public BigDecimal deriveCumulativeChargesOutstanding() { public BigDecimal deriveCumulativeFeeChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO; BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) { for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesOutstanding()); cumulativeValue = cumulativeValue.add(period.feeChargesOutstanding());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativePenaltyChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.penaltyChargesOutstanding());
} }
return cumulativeValue; return cumulativeValue;
} }
@@ -381,11 +381,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
for (LoanSchedulePeriodData scheduledLoanPeriod : loanSchedule.getPeriods()) { for (LoanSchedulePeriodData scheduledLoanPeriod : loanSchedule.getPeriods()) {
if (scheduledLoanPeriod.isRepaymentPeriod()) { if (scheduledLoanPeriod.isRepaymentPeriod()) {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment( LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanPeriod.periodNumber(), loan,
scheduledLoanPeriod.periodNumber(),
scheduledLoanPeriod.periodFromDate(),
scheduledLoanPeriod.periodDueDate(), scheduledLoanPeriod.periodDueDate(),
scheduledLoanPeriod.principalDue(), scheduledLoanPeriod.principalDue(),
scheduledLoanPeriod.interestDue(), scheduledLoanPeriod.interestDue(),
scheduledLoanPeriod.chargesDue()); scheduledLoanPeriod.feeChargesDue(),
scheduledLoanPeriod.penaltyChargesDue());
modifiedLoanRepaymentSchedule.add(installment); modifiedLoanRepaymentSchedule.add(installment);
} }
@@ -628,7 +631,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Transactional @Transactional
@Override @Override
public EntityIdentifier addLoanCharge(LoanChargeCommand command) { public EntityIdentifier addLoanCharge(final LoanChargeCommand command) {
this.context.authenticatedUser(); this.context.authenticatedUser();
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command); LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command);
@@ -119,16 +119,18 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
outstandingBalance = outstandingBalance.minus(principalForInstallment); outstandingBalance = outstandingBalance.minus(principalForInstallment);
Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment); final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount()); final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate, scheduledDueDate,
principalForInstallment.getAmount(), principalForInstallment.getAmount(),
outstandingBalance.getAmount(), outstandingBalance.getAmount(),
interestForInstallment.getAmount(), interestForInstallment.getAmount(),
chargesForInstallment.getAmount(), feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount()); totalInstallmentDue.getAmount());
periods.add(installment); periods.add(installment);
@@ -156,7 +158,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment); cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
} }
private Money cumulativeChargesDueWithin( private Money cumulativeFeeChargesDueWithin(
final LocalDate periodStart, final LocalDate periodStart,
final LocalDate periodEnd, final LocalDate periodEnd,
final Set<LoanCharge> loanCharges, final Set<LoanCharge> loanCharges,
@@ -165,7 +167,24 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
Money cumulative = Money.zero(monetaryCurrency); Money cumulative = Money.zero(monetaryCurrency);
for (LoanCharge loanCharge : loanCharges) { for (LoanCharge loanCharge : loanCharges) {
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd)) { if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
cumulative = cumulative.plus(loanCharge.amount());
}
}
return cumulative;
}
private Money cumulativePenaltyChargesDueWithin(
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) && loanCharge.isPenaltyCharge()) {
cumulative = cumulative.plus(loanCharge.amount()); cumulative = cumulative.plus(loanCharge.amount());
} }
} }
@@ -100,18 +100,18 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
outstandingBalance = outstandingBalance.minus(principalForInstallment); outstandingBalance = outstandingBalance.minus(principalForInstallment);
// FIXME - KW - charges Money feeChargesForInstallment = Money.zero(monetaryCurrency);
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money chargesForInstallment = Money.zero(monetaryCurrency); Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment); cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate, scheduledDueDate,
principalForInstallment.getAmount(), principalForInstallment.getAmount(),
outstandingBalance.getAmount(), outstandingBalance.getAmount(),
interestForInstallment.getAmount(), interestForInstallment.getAmount(),
chargesForInstallment.getAmount(), feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount()); totalInstallmentDue.getAmount());
periods.add(installment); periods.add(installment);
@@ -109,17 +109,19 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
outstandingBalance = outstandingBalance.minus(principalPerInstallment); outstandingBalance = outstandingBalance.minus(principalPerInstallment);
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency); Money feeChargesForInstallment = Money.zero(monetaryCurrency);
Money chargesForInstallment = Money.zero(monetaryCurrency); Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(chargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount()); Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount()).add(penaltyChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate, scheduledDueDate,
principalPerInstallment.getAmount(), principalPerInstallment.getAmount(),
outstandingBalance.getAmount(), outstandingBalance.getAmount(),
interestPerInstallment.getAmount(), interestPerInstallment.getAmount(),
chargesForInstallment.getAmount(), feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount()); totalInstallmentDue.getAmount());
periods.add(installment); periods.add(installment);
@@ -8,11 +8,13 @@ public class LoanRepaymentScheduleInstallmentBuilder {
private Loan loan = null; private Loan loan = null;
private Integer installmentNumber = Integer.valueOf(1); private Integer installmentNumber = Integer.valueOf(1);
private LocalDate fromDate = LocalDate.now();
private LocalDate dueDate = LocalDate.now(); private LocalDate dueDate = LocalDate.now();
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build(); private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private Money principal = new MoneyBuilder().build(); private Money principal = new MoneyBuilder().build();
private Money interest = new MoneyBuilder().build(); private Money interest = new MoneyBuilder().build();
private Money feeCharges = new MoneyBuilder().build(); private Money feeCharges = new MoneyBuilder().build();
private Money penaltyCharges = new MoneyBuilder().build();
private boolean completed = false; private boolean completed = false;
public LoanRepaymentScheduleInstallmentBuilder(MonetaryCurrency currencyDetail) { public LoanRepaymentScheduleInstallmentBuilder(MonetaryCurrency currencyDetail) {
@@ -22,7 +24,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
} }
public LoanRepaymentScheduleInstallment build() { public LoanRepaymentScheduleInstallment build() {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, dueDate, principal.getAmount(), interest.getAmount(), feeCharges.getAmount()); LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, installmentNumber, fromDate, dueDate, principal.getAmount(), interest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount());
if (completed) { if (completed) {
installment.payPrincipalComponent(principal); installment.payPrincipalComponent(principal);
installment.payInterestComponent(interest); installment.payInterestComponent(interest);