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})
public String retrieveAllCharges(@Context final UriInfo uriInfo){
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(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);
}
@@ -63,16 +63,15 @@ public class ChargesApiResource {
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
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());
boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
ChargeData charge = this.chargeReadPlatformService.retrieveCharge(chargeId);
if (template){
if (template) {
ChargeData templateData = this.chargeReadPlatformService.retrieveNewChargeDetails();
charge = new ChargeData(charge, templateData);
}
@@ -84,11 +83,12 @@ public class ChargesApiResource {
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
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);
}
@@ -98,9 +98,9 @@ public class ChargesApiResource {
@Produces({MediaType.APPLICATION_JSON})
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();
}
@@ -111,9 +111,9 @@ public class ChargesApiResource {
@Produces({MediaType.APPLICATION_JSON})
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();
}
@@ -1,8 +1,6 @@
package org.mifosng.platform.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -66,19 +64,6 @@ public class LoanProductsApiResource {
@Autowired
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
@Consumes({MediaType.APPLICATION_JSON})
@@ -98,9 +83,6 @@ public class LoanProductsApiResource {
public String retrieveAllLoanProducts(@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final Collection<LoanProductData> products = this.loanProductReadPlatformService.retrieveAllLoanProducts();
@@ -115,13 +97,10 @@ public class LoanProductsApiResource {
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails();
loanProduct = handleTemplate(loanProduct, responseParameters);
loanProduct = handleTemplate(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) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
if (template) {
loanProduct = handleTemplate(loanProduct, responseParameters);
loanProduct = handleTemplate(loanProduct);
}
return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct);
@@ -160,24 +136,22 @@ public class LoanProductsApiResource {
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",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions"));
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeOptions.removeAll(productData.charges());
List<CurrencyData> currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies();
List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
final List<CurrencyData> currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies();
final List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
final List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
final Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions);
@@ -58,14 +58,21 @@ public class LoanScheduleData {
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")
private final BigDecimal cumulativeChargesToDate;
private final BigDecimal cumulativeFeeChargesToDate;
@SuppressWarnings("unused")
private final BigDecimal cumulativeChargesPaid;
private final BigDecimal cumulativeFeeChargesPaid;
@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.
@@ -135,9 +142,13 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = null;
this.cumulativeChargesPaid = null;
this.cumulativeChargesOutstanding = null;
this.cumulativeFeeChargesToDate = null;
this.cumulativeFeeChargesPaid = null;
this.cumulativeFeeChargesOutstanding = null;
this.cumulativePenaltyChargesToDate = null;
this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesOutstanding = null;
this.totalCostOfLoan = null;
@@ -161,7 +172,7 @@ public class LoanScheduleData {
final BigDecimal cumulativePrincipalDue,
final BigDecimal cumulativePrincipalOutstanding,
final BigDecimal cumulativeInterestExpected,
final BigDecimal cumulativeChargesToDate,
final BigDecimal cumulativeFeeChargesToDate,
final BigDecimal totalExpectedRepayment) {
this.currency = currency;
this.periods = periods;
@@ -179,12 +190,16 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = null;
this.cumulativeInterestOutstanding = null;
this.cumulativeChargesToDate = cumulativeChargesToDate;
this.cumulativeChargesPaid = null;
this.cumulativeChargesOutstanding = null;
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeFeeChargesPaid = null;
this.cumulativeFeeChargesOutstanding = null;
if (cumulativeChargesToDate != null) {
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeChargesToDate);
this.cumulativePenaltyChargesToDate = null;
this.cumulativePenaltyChargesPaid = null;
this.cumulativePenaltyChargesOutstanding = null;
if (cumulativeFeeChargesToDate != null) {
this.totalCostOfLoan = cumulativeInterestExpected.add(cumulativeFeeChargesToDate);
} else if (cumulativeInterestExpected != null) {
this.totalCostOfLoan = cumulativeInterestExpected;
} else {
@@ -214,9 +229,12 @@ public class LoanScheduleData {
final BigDecimal cumulativeInterestWaived,
final BigDecimal cumulativeInterestWrittenOff,
final BigDecimal cumulativeInterestOutstanding,
final BigDecimal cumulativeChargesToDate,
final BigDecimal cumulativeChargesPaid,
final BigDecimal cumulativeChargesOutstanding,
final BigDecimal cumulativeFeeChargesToDate,
final BigDecimal cumulativeFeeChargesPaid,
final BigDecimal cumulativeFeeChargesOutstanding,
final BigDecimal cumulativePenaltyChargesToDate,
final BigDecimal cumulativePenaltyChargesPaid,
final BigDecimal cumulativePenaltyChargesOutstanding,
final BigDecimal totalCostOfLoan,
final BigDecimal totalExpectedRepayment,
final BigDecimal totalPaidToDate,
@@ -238,9 +256,12 @@ public class LoanScheduleData {
this.cumulativeInterestWrittenOff = cumulativeInterestWrittenOff;
this.cumulativeInterestWaived = cumulativeInterestWaived;
this.cumulativeInterestOutstanding = cumulativeInterestOutstanding;
this.cumulativeChargesToDate = cumulativeChargesToDate;
this.cumulativeChargesPaid = cumulativeChargesPaid;
this.cumulativeChargesOutstanding = cumulativeChargesOutstanding;
this.cumulativeFeeChargesToDate = cumulativeFeeChargesToDate;
this.cumulativeFeeChargesPaid = cumulativeFeeChargesPaid;
this.cumulativeFeeChargesOutstanding = cumulativeFeeChargesOutstanding;
this.cumulativePenaltyChargesToDate = cumulativePenaltyChargesToDate;
this.cumulativePenaltyChargesPaid = cumulativePenaltyChargesPaid;
this.cumulativePenaltyChargesOutstanding = cumulativePenaltyChargesOutstanding;
this.totalCostOfLoan = totalCostOfLoan;
this.totalExpectedRepayment = totalExpectedRepayment;
this.totalPaidToDate = totalPaidToDate;
@@ -1,6 +1,5 @@
package org.mifosng.platform.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -95,25 +94,6 @@ public class LoansApiResource {
@Autowired
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
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@@ -124,31 +104,24 @@ public class LoansApiResource {
@QueryParam("productId") final Long productId,
@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
Collection<LoanProductData> productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
Collection<EnumOptionData>loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
Collection<EnumOptionData>repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
Collection<EnumOptionData>interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
Collection<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
Collection<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
Collection<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
Collection<EnumOptionData>amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
Collection<EnumOptionData>interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
Collection<EnumOptionData>interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
Collection<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
Collection<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
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();
LoanBasicDetailsData loanBasicDetails;
@@ -183,11 +156,8 @@ public class LoansApiResource {
@PathParam("loanId") final Long loanId,
@Context final UriInfo uriInfo) {
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
@@ -202,19 +172,17 @@ public class LoansApiResource {
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
responseParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges"));
} else {
responseParameters.addAll(associationParameters);
associationParameters.addAll(Arrays.asList("repaymentSchedule", "transactions", "permissions", "convenienceData", "charges"));
}
if (responseParameters.contains("transactions")) {
if (associationParameters.contains("transactions")) {
final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId);
if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
loanRepayments = currentLoanRepayments;
}
}
if (responseParameters.contains("repaymentSchedule")) {
if (associationParameters.contains("repaymentSchedule")) {
DisbursementData singleDisbursement = loanBasicDetails.toDisburementData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, loanBasicDetails.getCurrency(),
@@ -227,14 +195,14 @@ public class LoansApiResource {
boolean isWaiveAllowed = totalOutstandingMoney.isGreaterThanZero() && (tolerance.isGreaterThan(totalOutstandingMoney) || tolerance.isEqualTo(totalOutstandingMoney));
if (responseParameters.contains("permissions")) {
if (associationParameters.contains("permissions")) {
loanRepaymentsCount = retrieveNonDisbursementTransactions(loanRepayments);
permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, isWaiveAllowed, loanRepaymentsCount);
}
convenienceDataRequired = true;
}
if (responseParameters.contains("charges")) {
if (associationParameters.contains("charges")) {
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) {
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> repaymentFrequencyTypeOptions = new ArrayList<EnumOptionData>();
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = new ArrayList<EnumOptionData>();
Collection<EnumOptionData> amortizationTypeOptions = new ArrayList<EnumOptionData>();
Collection<EnumOptionData> interestTypeOptions = new ArrayList<EnumOptionData>();
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = new ArrayList<EnumOptionData>();
Collection<FundData> fundOptions = new ArrayList<FundData>();
Collection<EnumOptionData> interestRateFrequencyTypeOptions = null;
Collection<EnumOptionData> amortizationTypeOptions = null;
Collection<EnumOptionData> interestTypeOptions = null;
Collection<EnumOptionData> interestCalculationPeriodTypeOptions = null;
Collection<FundData> fundOptions = null;
Collection<StaffData> allowedLoanOfficers = null;
Collection<ChargeData> chargeOptions = null;
ChargeData chargeTemplate = null;
Collection<StaffData> allowedLoanOfficers = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if(template) {
responseParameters.addAll(
Arrays.asList("productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions",
"repaymentStrategyOptions", "chargeOptions", "chargeTemplate", "loanOfficerOptions")
);
productOptions = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
@@ -275,7 +236,8 @@ public class LoansApiResource {
fundOptions = this.fundReadPlatformService.retrieveAllFunds();
repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.getClientOfficeId());
chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
}
@@ -461,9 +423,6 @@ public class LoansApiResource {
@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalTransactionResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
LoanTransactionData transactionData = null;
@@ -493,9 +452,6 @@ public class LoansApiResource {
@Context final UriInfo uriInfo) {
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalTransactionResponseParameters);
}
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransactionDetails(loanId, transactionId);
@@ -530,9 +486,9 @@ public class LoansApiResource {
@PathParam("loanId") final Long loanId,
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();
}
@@ -542,12 +498,14 @@ public class LoansApiResource {
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
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();
LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions);
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
final LoanChargeData loanChargeTemplate = LoanChargeData.template(chargeOptions);
return this.apiJsonSerializerService.serializeLoanChargeDataToJson(prettyPrint, responseParameters, loanChargeTemplate);
}
@@ -3,6 +3,9 @@ package org.mifosng.platform.api.commands;
import java.math.BigDecimal;
import java.util.Set;
/**
* Immutable data object for creating and modifying defined charges.
*/
public class ChargeCommand {
private final Long id;
@@ -14,12 +17,22 @@ public class ChargeCommand {
private final Integer chargeAppliesTo;
private final Integer chargeCalculationType;
private final boolean penalty;
private final boolean active;
private final Set<String> modifiedParameters;
public ChargeCommand(Set<String> modifiedParameters, Long id, String name, BigDecimal amount, String currencyCode,
Integer chargeTimeType, Integer chargeAppliesTo, Integer chargeCalculationType, boolean active) {
public ChargeCommand(
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.name = name;
this.amount = amount;
@@ -28,6 +41,7 @@ public class ChargeCommand {
this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType;
this.modifiedParameters = modifiedParameters;
this.penalty = penalty;
this.active = active;
}
@@ -62,8 +76,16 @@ public class ChargeCommand {
public Integer getChargeTimeType() {
return chargeTimeType;
}
public boolean isPenalty() {
return penalty;
}
public boolean isFee() {
return !penalty;
}
public boolean isActive() {
public boolean isActive() {
return active;
}
@@ -91,7 +113,11 @@ public class ChargeCommand {
return this.modifiedParameters.contains("chargeCalculationType");
}
public boolean isPenaltyChanged(){
return this.modifiedParameters.contains("penalty");
}
public boolean isActiveChanged(){
return this.modifiedParameters.contains("active");
}
}
}
@@ -11,6 +11,7 @@ public class ChargeData {
private final Long id;
private final String name;
private final boolean active;
private final boolean penalty;
private final CurrencyData currency;
private final BigDecimal amount;
private final EnumOptionData chargeTimeType;
@@ -27,22 +28,24 @@ public class ChargeData {
final List<EnumOptionData> chargeCalculationTypeOptions,
final List<EnumOptionData> chargeAppliesToOptions,
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);
}
public ChargeData(ChargeData charge, ChargeData template){
this(charge.getId(), charge.getName(), charge.getAmount(), charge.getCurrency(), charge.getChargeTimeType(),
charge.getChargeAppliesTo(), charge.getChargeCalculationType(), charge.isActive(),
template.getCurrencyOptions(), template.getChargeCalculationTypeOptions(),
template.getChargeAppliesToOptions(), template.getChargeTimeTypeOptions());
public ChargeData(final ChargeData charge, final ChargeData template){
this(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType,
charge.chargeAppliesTo, charge.chargeCalculationType,
charge.penalty, charge.active,
template.currencyOptions, template.chargeCalculationTypeOptions,
template.chargeAppliesToOptions, template.chargeTimeTypeOptions);
}
public ChargeData(final Long id, final String name,
final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeAppliesTo,
final EnumOptionData chargeCalculationType, final boolean active) {
final EnumOptionData chargeCalculationType, final boolean penalty, final boolean active) {
this.id = id;
this.name = name;
this.amount = amount;
@@ -50,6 +53,7 @@ public class ChargeData {
this.chargeTimeType = chargeTimeType;
this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType;
this.penalty = penalty;
this.active = active;
this.currencyOptions = null;
this.chargeCalculationTypeOptions = null;
@@ -61,7 +65,9 @@ public class ChargeData {
final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeAppliesTo,
final EnumOptionData chargeCalculationType, final boolean active,
final EnumOptionData chargeCalculationType,
final boolean penalty,
final boolean active,
final List<CurrencyData> currencyOptions,
final List<EnumOptionData> chargeCalculationTypeOptions,
final List<EnumOptionData> chargeAppliesToOptions,
@@ -73,6 +79,7 @@ public class ChargeData {
this.chargeTimeType = chargeTimeType;
this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType;
this.penalty = penalty;
this.active = active;
this.currencyOptions = currencyOptions;
this.chargeCalculationTypeOptions = chargeCalculationTypeOptions;
@@ -88,10 +95,10 @@ public class ChargeData {
return name;
}
public boolean isActive() {
return active;
public boolean isPenalty() {
return this.penalty;
}
public BigDecimal getAmount() {
return amount;
}
@@ -104,34 +111,14 @@ public class ChargeData {
return chargeTimeType;
}
public EnumOptionData getChargeAppliesTo() {
return chargeAppliesTo;
}
public EnumOptionData getChargeCalculationType() {
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
public boolean equals(Object obj) {
ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.getId());
return this.id.equals(chargeData.id);
}
@Override
@@ -78,8 +78,8 @@ public class LoanBasicDetailsData {
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
for (ChargeData charge : product.charges()) {
charges.add(LoanChargeData.newChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(),
charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType()));
charges.add(LoanChargeData.newLoanChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(),
charge.getAmount(), charge.getChargeTimeType(), charge.getChargeCalculationType(), charge.isPenalty()));
}
return new LoanBasicDetailsData(
@@ -51,21 +51,25 @@ public class LoanChargeData {
@SuppressWarnings("unused")
private final Collection<ChargeData> chargeOptions;
@SuppressWarnings("unused")
private final boolean penalty;
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)
*/
public static LoanChargeData newChargeDetails(
public static LoanChargeData newLoanChargeDetails(
final Long chargeId,
final String name,
final CurrencyData currency,
final BigDecimal value,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType) {
return new LoanChargeData(null, chargeId, name, currency, value, chargeTimeType, chargeCalculationType, null);
final EnumOptionData chargeCalculationType,
final boolean penalty) {
return new LoanChargeData(null, chargeId, name, currency, value, chargeTimeType, chargeCalculationType, null, penalty);
}
public LoanChargeData(
@@ -80,7 +84,8 @@ public class LoanChargeData {
final LocalDate dueAsOfDate,
final EnumOptionData chargeCalculationType,
final BigDecimal percentage,
final BigDecimal amountPercentageAppliedTo) {
final BigDecimal amountPercentageAppliedTo,
final boolean penalty) {
this.id = id;
this.chargeId = chargeId;
this.name = name;
@@ -93,6 +98,7 @@ public class LoanChargeData {
this.chargeCalculationType = chargeCalculationType;
this.percentage = percentage;
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
this.penalty = penalty;
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
this.amountOrPercentage = this.percentage;
@@ -111,7 +117,8 @@ public class LoanChargeData {
final BigDecimal amount,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType,
final Collection<ChargeData> chargeOptions) {
final Collection<ChargeData> chargeOptions,
final boolean penalty) {
this.id = id;
this.chargeId = chargeId;
this.name = name;
@@ -124,6 +131,7 @@ public class LoanChargeData {
this.chargeCalculationType = chargeCalculationType;
this.percentage = null;
this.amountPercentageAppliedTo = null;
this.penalty = penalty;
if (chargeCalculationType != null && chargeCalculationType.getId().intValue() > 1) {
this.amountOrPercentage = this.percentage;
@@ -24,7 +24,6 @@ public class LoanSchedulePeriodData {
private final BigDecimal principalOutstanding;
@SuppressWarnings("unused")
private final BigDecimal principalLoanBalanceOutstanding;
@SuppressWarnings("unused")
private final BigDecimal interestOriginalDue;
private final BigDecimal interestDue;
@@ -32,14 +31,16 @@ public class LoanSchedulePeriodData {
private final BigDecimal interestWaived;
private final BigDecimal interestWrittenOff;
private final BigDecimal interestOutstanding;
private final BigDecimal feeChargesDue;
private final BigDecimal feeChargesPaid;
@SuppressWarnings("unused")
private final BigDecimal feeChargesWaived;
@SuppressWarnings("unused")
private final BigDecimal feeChargesWrittenOff;
private final BigDecimal feeChargesOutstanding;
private final BigDecimal penaltyChargesDue;
private final BigDecimal penaltyChargesPaid;
private final BigDecimal penaltyChargesWaived;
private final BigDecimal penaltyChargesWrittenOff;
private final BigDecimal penaltyChargesOutstanding;
@SuppressWarnings("unused")
private final BigDecimal totalOriginalDueForPeriod;
@@ -53,15 +54,17 @@ public class LoanSchedulePeriodData {
private final BigDecimal totalWrittenOffForPeriod;
private final BigDecimal totalOutstandingForPeriod;
private final BigDecimal totalOverdue;
@SuppressWarnings("unused")
private final BigDecimal totalActualCostOfLoanForPeriod;
public static LoanSchedulePeriodData disbursementOnlyPeriod(
final LocalDate disbursementDate,
final BigDecimal principalDisbursed,
final BigDecimal chargesDueAtTimeOfDisbursement,
final BigDecimal feeChargesDueAtTimeOfDisbursement,
final boolean isDisbursed) {
Integer periodNumber = Integer.valueOf(0);
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(
@@ -71,10 +74,12 @@ public class LoanSchedulePeriodData {
final BigDecimal principalDue,
final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal chargesDueForPeriod,
final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
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(
@@ -97,17 +102,24 @@ public class LoanSchedulePeriodData {
final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) {
final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
return new LoanSchedulePeriodData(periodNumber, fromDate, dueDate,
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
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;
if (isDisbursed) {
this.feeChargesPaid = chargesDueAtTimeOfDisbursement;
this.feeChargesWaived = BigDecimal.ZERO;
this.feeChargesWrittenOff = BigDecimal.ZERO;
this.feeChargesOutstanding = BigDecimal.ZERO;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
} else {
this.feeChargesPaid = BigDecimal.ZERO;
this.feeChargesWaived = BigDecimal.ZERO;
this.feeChargesWrittenOff = BigDecimal.ZERO;
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = chargesDueAtTimeOfDisbursement;
}
this.penaltyChargesDue = null;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = this.feeChargesPaid;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = this.feeChargesOutstanding;
this.totalActualCostOfLoanForPeriod = this.feeChargesDue;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
@@ -176,7 +195,8 @@ public class LoanSchedulePeriodData {
final BigDecimal principalOriginalDue,
final BigDecimal principalOutstanding,
final BigDecimal interestDueOnPrincipalOutstanding,
final BigDecimal chargesDueForPeriod,
final BigDecimal feeChargesDueForPeriod,
final BigDecimal penaltyChargesDueForPeriod,
final BigDecimal totalDueForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
@@ -201,18 +221,26 @@ public class LoanSchedulePeriodData {
this.interestWrittenOff = null;
this.interestOutstanding = interestDueOnPrincipalOutstanding;
this.feeChargesDue = chargesDueForPeriod;
this.feeChargesDue = feeChargesDueForPeriod;
this.feeChargesPaid = null;
this.feeChargesWaived = null;
this.feeChargesWrittenOff = null;
this.feeChargesOutstanding = null;
this.penaltyChargesDue = penaltyChargesDueForPeriod;
this.penaltyChargesPaid = null;
this.penaltyChargesWaived = null;
this.penaltyChargesWrittenOff = null;
this.penaltyChargesOutstanding = null;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = BigDecimal.ZERO;
this.totalWaivedForPeriod = null;
this.totalWrittenOffForPeriod = null;
this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod);
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
@@ -242,11 +270,17 @@ public class LoanSchedulePeriodData {
final BigDecimal feeChargesWaived,
final BigDecimal feeChargesWrittenOff,
final BigDecimal feeChargesOutstanding,
final BigDecimal penaltyChargesDue,
final BigDecimal penaltyChargesPaid,
final BigDecimal penaltyChargesWaived,
final BigDecimal penaltyChargesWrittenOff,
final BigDecimal penaltyChargesOutstanding,
final BigDecimal totalDueForPeriod,
final BigDecimal totalPaid,
final BigDecimal totalWaived,
final BigDecimal totalWrittenOff,
final BigDecimal totalOutstanding) {
final BigDecimal totalOutstanding,
final BigDecimal totalActualCostOfLoanForPeriod) {
this.period = periodNumber;
this.fromDate = fromDate;
this.dueDate = dueDate;
@@ -276,12 +310,19 @@ public class LoanSchedulePeriodData {
this.feeChargesWrittenOff = feeChargesWrittenOff;
this.feeChargesOutstanding = feeChargesOutstanding;
this.penaltyChargesDue = penaltyChargesDue;
this.penaltyChargesPaid = penaltyChargesPaid;
this.penaltyChargesWaived = penaltyChargesWaived;
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
this.penaltyChargesOutstanding = penaltyChargesOutstanding;
this.totalOriginalDueForPeriod = totalDueForPeriod;
this.totalDueForPeriod = totalDueForPeriod;
this.totalPaidForPeriod = totalPaid;
this.totalWaivedForPeriod = totalWaived;
this.totalWrittenOffForPeriod = totalWrittenOff;
this.totalOutstandingForPeriod = totalOutstanding;
this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod;
if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
@@ -310,6 +351,10 @@ public class LoanSchedulePeriodData {
return this.period;
}
public LocalDate periodFromDate() {
return this.fromDate;
}
public LocalDate periodDueDate() {
return this.dueDate;
}
@@ -358,17 +403,45 @@ public class LoanSchedulePeriodData {
return defaultToZeroIfNull(this.interestOutstanding);
}
public BigDecimal chargesDue() {
public BigDecimal 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);
}
public BigDecimal chargesOutstanding() {
public BigDecimal feeChargesOutstanding() {
return defaultToZeroIfNull(this.feeChargesOutstanding);
}
public BigDecimal penaltyChargesDue() {
return defaultToZeroIfNull(this.penaltyChargesDue);
}
public BigDecimal penaltyChargesWaived() {
return defaultToZeroIfNull(this.penaltyChargesWaived);
}
public BigDecimal penaltyChargesWrittenOff() {
return defaultToZeroIfNull(this.penaltyChargesWrittenOff);
}
public BigDecimal penaltyChargesPaid() {
return defaultToZeroIfNull(this.penaltyChargesPaid);
}
public BigDecimal penaltyChargesOutstanding() {
return defaultToZeroIfNull(this.penaltyChargesOutstanding);
}
public BigDecimal totalOverdue() {
return defaultToZeroIfNull(this.totalOverdue);
@@ -72,31 +72,32 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
throw new InvalidJsonException();
}
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType();
final Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>(Arrays.asList("name", "amount", "locale", "currencyCode",
final Set<String> supportedParams = new HashSet<String>(
Arrays.asList("name", "amount", "locale", "currencyCode",
"currencyOptions", "chargeAppliesTo", "chargeTimeType",
"chargeCalculationType", "chargeCalculationTypeOptions", "active"
"chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active"
));
checkForUnsupportedParameters(requestMap, supportedParams);
Set<String> modifiedParameters = new HashSet<String>();
String name = extractStringParameter("name", requestMap, modifiedParameters);
BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters);
String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters);
final String name = extractStringParameter("name", requestMap, modifiedParameters);
final BigDecimal amount = extractBigDecimalParameter("amount", requestMap, modifiedParameters);
final String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters);
Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters);
Integer chargeAppliesTo = extractIntegerParameter("chargeAppliesTo", requestMap, modifiedParameters);
Integer chargeCalculationType = extractIntegerParameter("chargeCalculationType", requestMap, modifiedParameters);
final Integer chargeTimeType = extractIntegerParameter("chargeTimeType", requestMap, modifiedParameters);
final Integer chargeAppliesTo = extractIntegerParameter("chargeAppliesTo", 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,
currencyCode, chargeTimeType, chargeAppliesTo, chargeCalculationType, active);
currencyCode, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active);
}
@Override
@@ -188,7 +188,7 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
Arrays.asList("id", "type", "date", "currency", "amount"));
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",
"chargeCalculationType", "chargeCalculationTypeOptions",
"chargeAppliesToOptions", "chargeTimeTypeOptions", "currencyOptions"));
@@ -38,7 +38,7 @@ public class GoogleGsonSerializerHelper {
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.
final Set<String> differentParametersDetectedSet = new HashSet<String>(responseParameters);
differentParametersDetectedSet.removeAll(supportedParameters);
@@ -32,15 +32,23 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "charge_calculation_enum")
private Integer chargeCalculation;
@Column(name = "is_penalty", nullable = false)
private boolean penalty;
@Column(name = "is_active", nullable = false)
private boolean active;
@Column(name = "is_deleted", nullable=false)
private boolean deleted = false;
public static Charge createNew(final String name, final BigDecimal amount, final String currencyCode,
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean active) {
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTime, chargeCalculationType, active);
public static Charge createNew(
final String name, final BigDecimal amount, final String currencyCode,
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() {
@@ -49,13 +57,14 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
private Charge(final String name, final BigDecimal amount, final String currencyCode,
final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime,
final ChargeCalculationType chargeCalculationType, final boolean active) {
final ChargeCalculationType chargeCalculationType, final boolean penalty, boolean active) {
this.name = name;
this.amount = amount;
this.currencyCode = currencyCode;
this.chargeAppliesTo = chargeAppliesTo.getValue();
this.chargeTime = chargeTime.getValue();
this.chargeCalculation = chargeCalculationType.getValue();
this.penalty = penalty;
this.active = active;
}
@@ -82,6 +91,10 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
public boolean isActive() {
return active;
}
public boolean isPenalty() {
return penalty;
}
public boolean isDeleted() {
return deleted;
@@ -126,4 +139,4 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
this.deleted = true;
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("chargeTimeType").value(command.getChargeTimeType()).notNull().inMinMaxRange(1, 2);
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()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
@@ -13,7 +13,7 @@ public interface ChargeReadPlatformService {
ChargeData retrieveNewChargeDetails();
Collection<ChargeData> retrieveLoanApplicableCharges();
Collection<ChargeData> retrieveLoanApplicableCharges(boolean feeChargesOnly);
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId);
@@ -101,18 +101,22 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
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});
}
@Override
public Collection<ChargeData> retrieveLoanApplicableCharges() {
public Collection<ChargeData> retrieveLoanApplicableCharges(final boolean feeChargesOnly) {
this.context.authenticatedUser();
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()});
}
@@ -132,7 +136,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
public String chargeSchema(){
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_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.internationalized_name_code as currencyNameCode from m_charge c " +
"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 EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
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.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " +
"lc.charge_time_enum as chargeTime, " +
"lc.is_penalty as penalty, " +
"lc.due_for_collection_as_of_date as dueAsOfDate, " +
"lc.charge_calculation_enum as chargeCalculation, " +
"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 String name = rs.getString("name");
final BigDecimal amount = rs.getBigDecimal("amountDue");
BigDecimal amountPaid = rs.getBigDecimal("amountPaid");
if (amountPaid == null) {
amountPaid = BigDecimal.ZERO;
}
final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal percentageOf = rs.getBigDecimal("percentageOf");
final BigDecimal amountPercentageAppliedTo = rs.getBigDecimal("amountPercentageAppliedTo");
final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
@@ -219,8 +222,9 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo);
final boolean penalty = rs.getBoolean("penalty");
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 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);
@@ -378,11 +378,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
if (scheduledLoanInstallment.isRepaymentPeriod()) {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
this, scheduledLoanInstallment.periodNumber(),
this,
scheduledLoanInstallment.periodNumber(),
scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(),
scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(),
scheduledLoanInstallment.chargesDue());
scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue());
this.addRepaymentScheduleInstallment(installment);
}
}
@@ -60,6 +60,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal amountOutstanding;
@Column(name = "is_penalty", nullable=false)
private boolean penaltyCharge = false;
@SuppressWarnings("unused")
@Column(name = "is_paid_derived", nullable=false)
@@ -83,11 +86,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
protected LoanCharge() {
//
}
public LoanCharge(final Charge chargeDefinition, final LoanChargeCommand command, final BigDecimal loanPrincipal) {
this.loan = null;
this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty();
if (command.isChargeTimeTypeChanged()) {
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) {
this.loan = loan;
this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty();
if (command.isChargeTimeTypeChanged()){
this.chargeTime = command.getChargeTimeType();
} else {
@@ -344,4 +347,12 @@ public class LoanCharge extends AbstractPersistable<Long> {
final LocalDate 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")
private Loan loan;
@Column(name = "installment")
@Column(name = "installment", nullable=false)
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;
@Column(name = "interest_amount", scale=6, precision=19)
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)
@Column(name = "principal_completed_derived", scale=6, precision=19, nullable=true)
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;
@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;
@Column(name = "interest_waived_derived", scale = 6, precision = 19)
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable=true)
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;
@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;
@Temporal(TemporalType.DATE)
@Column(name = "duedate")
private final Date dueDate;
protected LoanRepaymentScheduleInstallment() {
this.loan = null;
this.installmentNumber = null;
this.dueDate = null;
this.principal = null;
this.interest = null;
this.principalCompleted = null;
this.interestCompleted = null;
this.installmentNumber = null;
this.fromDate = null;
this.dueDate = null;
this.completed = false;
}
public LoanRepaymentScheduleInstallment(
final Loan loan,
final Integer installmentNumber,
final LocalDate fromDate,
final LocalDate dueDate,
final BigDecimal principal,
final BigDecimal interest,
final BigDecimal feeCharges) {
final BigDecimal feeCharges,
final BigDecimal penaltyCharges) {
this.loan = loan;
this.installmentNumber = installmentNumber;
this.fromDate = fromDate.toDateMidnight().toDate();
this.dueDate = dueDate.toDateMidnight().toDate();
this.principal = defaultToNullIfZero(principal);
this.interest = defaultToNullIfZero(interest);
this.feeCharges = defaultToNullIfZero(feeCharges);
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
this.completed = false;
}
@@ -219,11 +223,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
}
public void resetDerivedComponents() {
this.principalCompleted = BigDecimal.ZERO;
this.principalWrittenOff = BigDecimal.ZERO;
this.interestCompleted = BigDecimal.ZERO;
this.interestWaived = BigDecimal.ZERO;
this.interestWrittenOff = BigDecimal.ZERO;
this.principalCompleted = null;
this.principalWrittenOff = null;
this.interestCompleted = null;
this.interestWaived = null;
this.interestWrittenOff = null;
this.completed = false;
}
@@ -241,6 +245,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
interestPortionOfTransaction = interestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestCompleted = defaultToNullIfZero(this.interestCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return interestPortionOfTransaction;
@@ -260,6 +266,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
principalPortionOfTransaction = principalPortionOfTransaction.plus(transactionAmountRemaining);
}
this.principalCompleted = defaultToNullIfZero(this.principalCompleted);
this.completed = getTotalOutstanding(currency).isZero();
return principalPortionOfTransaction;
@@ -278,6 +286,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(transactionAmountRemaining);
}
this.interestWaived = defaultToNullIfZero(this.interestWaived);
this.completed = getTotalOutstanding(currency).isZero();
return waivedInterestPortionOfTransaction;
@@ -286,7 +296,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public Money writeOffOutstandingPrincipal(final MonetaryCurrency currency) {
final Money principalDue = getPrincipalOutstanding(currency);
this.principalWrittenOff = principalDue.getAmount();
this.principalWrittenOff = defaultToNullIfZero(principalDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return principalDue;
@@ -295,7 +305,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
public Money writeOffOutstandingInterest(final MonetaryCurrency currency) {
final Money interestDue = getInterestOutstanding(currency);
this.interestWrittenOff = interestDue.getAmount();
this.interestWrittenOff = defaultToNullIfZero(interestDue.getAmount());
this.completed = getTotalOutstanding(currency).isZero();
return interestDue;
@@ -305,7 +315,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
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.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.
* @param localDate
*/
public void reprocess(
final MonetaryCurrency currency,
@@ -42,15 +41,15 @@ public class LoanScheduleWrapper {
for (LoanRepaymentScheduleInstallment period : repaymentPeriods) {
// FIXME - kw - also need to handle case where some of these charges are paid/writtenoff/waived so that those components are updated on repayment period.
final Money feeChargesDueForRepaymentPeriod = cumulativeChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
// if (feeChargesDueForRepaymentPeriod.isGreaterThanZero()) {
period.updateChargePortion(feeChargesDueForRepaymentPeriod);
// }
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(), loanCharges, currency);
period.updateChargePortion(feeChargesDueForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod);
startDate = period.getDueDate();
}
}
private Money cumulativeChargesDueWithin(
private Money cumulativeFeeChargesDueWithin(
final LocalDate periodStart,
final LocalDate periodEnd,
final Set<LoanCharge> loanCharges,
@@ -59,7 +58,24 @@ public class LoanScheduleWrapper {
Money cumulative = Money.zero(monetaryCurrency);
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());
}
}
@@ -137,11 +137,14 @@ public class LoanAssembler {
if (scheduledLoanInstallment.isRepaymentPeriod()) {
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanInstallment.periodNumber(),
loan,
scheduledLoanInstallment.periodNumber(),
scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(),
scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(),
scheduledLoanInstallment.chargesDue());
scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue());
loan.addRepaymentScheduleInstallment(installment);
}
@@ -98,7 +98,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
final CurrencyData currency,
final DisbursementData disbursement,
final BigDecimal totalChargesAtDisbursement,
final BigDecimal totalFeeChargesAtDisbursement,
final BigDecimal inArrearsTolerance) {
try {
@@ -107,7 +107,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement);
final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment";
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), 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 });
@@ -128,16 +128,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final BigDecimal cumulativeInterestWaived = wrapper.deriveCumulativeInterestWaived();
final BigDecimal cumulativeInterestWrittenOff = wrapper.deriveCumulativeInterestWrittenOff();
final BigDecimal cumulativeInterestOutstanding = wrapper.deriveCumulativeInterestOutstanding();
final BigDecimal cumulativeChargesToDate = wrapper.deriveCumulativeChargesToDate();
final BigDecimal cumulativeChargesPaid = wrapper.deriveCumulativeChargesPaid();
final BigDecimal cumulativeChargesOutstanding = wrapper.deriveCumulativeChargesOutstanding();
final BigDecimal cumulativeFeeChargesToDate = wrapper.deriveCumulativeFeeChargesToDate();
final BigDecimal cumulativeFeeChargesPaid = wrapper.deriveCumulativeFeeChargesPaid();
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 totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeChargesPaid);
final BigDecimal totalPaidToDate = cumulativePrincipalPaid.add(cumulativeInterestPaid).add(cumulativeFeeChargesPaid);
final BigDecimal totalWaivedToDate = cumulativeInterestWaived;
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();
@@ -154,7 +157,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return new LoanScheduleData(currency, periods, loanTermInDays,
cumulativePrincipalDisbursed, cumulativePrincipalDue, cumulativePrincipalPaid, cumulativePrincipalWrittenOff, cumulativePrincipalOutstanding,
cumulativeInterestExpected, cumulativeInterestPaid, cumulativeInterestWaived, cumulativeInterestWrittenOff, cumulativeInterestOutstanding,
cumulativeChargesToDate, cumulativeChargesPaid, cumulativeChargesOutstanding,
cumulativeFeeChargesToDate, cumulativeFeeChargesPaid, cumulativeFeeChargesOutstanding,
cumulativePenaltyChargesToDate, cumulativePenaltyChargesPaid, cumulativePenaltyChargesOutstanding,
totalCostOfLoan, totalExpectedRepayment, totalPaidToDate, totalWaivedToDate, totalWrittenOffToDate, totalOutstanding, totalInArrears);
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
@@ -516,10 +520,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
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.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 "
+ " 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 Integer period = JdbcSupport.getInteger(rs, "period");
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
final BigDecimal principalPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalPaid");
@@ -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?
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 interestWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWaived");
final BigDecimal interestWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestWrittenOff");
// TODO - KW - rather than calculate this here should we put calculation on derived column on table like other columns?
final BigDecimal interestOutstanding = interestDue.subtract(interestPaid).subtract(interestWaived).subtract(interestWrittenOff);
final BigDecimal 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 feeChargesWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesWaived");
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 totalWaivedForPeriod = interestWaived.add(feeChargesWaived);
final BigDecimal totalWrittenOffForPeriod = principalWrittenOff.add(interestWrittenOff).add(feeChargesWrittenOff);
final BigDecimal totalOutstandingForPeriod = principalOutstanding.add(interestOutstanding).add(feeChargesOutstanding);
final LocalDate fromDate = this.lastDueDate;
final BigDecimal totalActualCostOfLoanForPeriod = interestActualDue.add(feeChargesActualDue).add(penaltyChargesActualDue);
if (fromDate == null) {
fromDate = this.lastDueDate;
}
final BigDecimal outstandingPrincipleBalanceOfLoan = outstandingLoanPrincipalBalance.subtract(principalDue);
// update based on current period values
@@ -567,9 +588,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate,
principalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipleBalanceOfLoan,
interestDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod);
interestExpectedDue, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
feeChargesExpectedDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding,
penaltyChargesExpectedDue, penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding,
totalDueForPeriod, totalPaidForPeriod, totalWaivedForPeriod, totalWrittenOffForPeriod, totalOutstandingForPeriod, totalActualCostOfLoanForPeriod);
}
}
@@ -93,26 +93,50 @@ public class LoanSchedulePeriodDataWrapper {
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesToDate() {
public BigDecimal deriveCumulativeFeeChargesToDate() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesDue());
cumulativeValue = cumulativeValue.add(period.feeChargesDue());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesPaid() {
public BigDecimal deriveCumulativeFeeChargesPaid() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
for (LoanSchedulePeriodData period : this.periods) {
cumulativeValue = cumulativeValue.add(period.chargesPaid());
cumulativeValue = cumulativeValue.add(period.feeChargesPaid());
}
return cumulativeValue;
}
public BigDecimal deriveCumulativeChargesOutstanding() {
public BigDecimal deriveCumulativeFeeChargesOutstanding() {
BigDecimal cumulativeValue = BigDecimal.ZERO;
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;
}
@@ -381,11 +381,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
for (LoanSchedulePeriodData scheduledLoanPeriod : loanSchedule.getPeriods()) {
if (scheduledLoanPeriod.isRepaymentPeriod()) {
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanPeriod.periodNumber(),
loan,
scheduledLoanPeriod.periodNumber(),
scheduledLoanPeriod.periodFromDate(),
scheduledLoanPeriod.periodDueDate(),
scheduledLoanPeriod.principalDue(),
scheduledLoanPeriod.interestDue(),
scheduledLoanPeriod.chargesDue());
scheduledLoanPeriod.feeChargesDue(),
scheduledLoanPeriod.penaltyChargesDue());
modifiedLoanRepaymentSchedule.add(installment);
}
@@ -628,7 +631,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Transactional
@Override
public EntityIdentifier addLoanCharge(LoanChargeCommand command) {
public EntityIdentifier addLoanCharge(final LoanChargeCommand command) {
this.context.authenticatedUser();
LoanChargeCommandValidator validator = new LoanChargeCommandValidator(command);
@@ -119,16 +119,18 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
outstandingBalance = outstandingBalance.minus(principalForInstallment);
Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate,
principalForInstallment.getAmount(),
outstandingBalance.getAmount(),
interestForInstallment.getAmount(),
chargesForInstallment.getAmount(),
feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount());
periods.add(installment);
@@ -156,7 +158,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
}
private Money cumulativeChargesDueWithin(
private Money cumulativeFeeChargesDueWithin(
final LocalDate periodStart,
final LocalDate periodEnd,
final Set<LoanCharge> loanCharges,
@@ -165,7 +167,24 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
Money cumulative = Money.zero(monetaryCurrency);
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());
}
}
@@ -100,18 +100,18 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
outstandingBalance = outstandingBalance.minus(principalForInstallment);
// FIXME - KW - charges
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
Money chargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(chargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
Money feeChargesForInstallment = Money.zero(monetaryCurrency);
Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate,
principalForInstallment.getAmount(),
outstandingBalance.getAmount(),
interestForInstallment.getAmount(),
chargesForInstallment.getAmount(),
feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount());
periods.add(installment);
@@ -109,17 +109,19 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
outstandingBalance = outstandingBalance.minus(principalPerInstallment);
// Money chargesForInstallment = cumulativeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
Money chargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(chargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(chargesForInstallment.getAmount());
Money feeChargesForInstallment = Money.zero(monetaryCurrency);
Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount()).add(penaltyChargesForInstallment.getAmount());
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
scheduledDueDate,
principalPerInstallment.getAmount(),
outstandingBalance.getAmount(),
interestPerInstallment.getAmount(),
chargesForInstallment.getAmount(),
feeChargesForInstallment.getAmount(),
penaltyChargesForInstallment.getAmount(),
totalInstallmentDue.getAmount());
periods.add(installment);
@@ -8,11 +8,13 @@ public class LoanRepaymentScheduleInstallmentBuilder {
private Loan loan = null;
private Integer installmentNumber = Integer.valueOf(1);
private LocalDate fromDate = LocalDate.now();
private LocalDate dueDate = LocalDate.now();
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private Money principal = new MoneyBuilder().build();
private Money interest = new MoneyBuilder().build();
private Money feeCharges = new MoneyBuilder().build();
private Money penaltyCharges = new MoneyBuilder().build();
private boolean completed = false;
public LoanRepaymentScheduleInstallmentBuilder(MonetaryCurrency currencyDetail) {
@@ -22,7 +24,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
}
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) {
installment.payPrincipalComponent(principal);
installment.payInterestComponent(interest);