add relationship between loan product and the loan transaction processing strategy. its optional by default for loan product and loans. If no value selected it will default to the the 'Mifos style' repayment strategy which is pay off interest, then principal in all cases.

This commit is contained in:
Keith Woodlock
2012-07-30 13:01:13 +01:00
parent 864df5eeef
commit 97bf8e2f93
15 changed files with 268 additions and 50 deletions
@@ -63,6 +63,18 @@ CREATE TABLE `ref_loan_status` (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` bigint(20) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
-- DDL for organisation wide related concepts -- DDL for organisation wide related concepts
CREATE TABLE `org_fund` ( CREATE TABLE `org_fund` (
`id` bigint(20) NOT NULL AUTO_INCREMENT, `id` bigint(20) NOT NULL AUTO_INCREMENT,
@@ -216,6 +228,7 @@ CREATE TABLE `portfolio_client` (
CONSTRAINT `FKCE00CAB3E0DD567A` FOREIGN KEY (`office_id`) REFERENCES `org_office` (`id`) CONSTRAINT `FKCE00CAB3E0DD567A` FOREIGN KEY (`office_id`) REFERENCES `org_office` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `portfolio_product_loan` ( CREATE TABLE `portfolio_product_loan` (
`id` bigint(20) NOT NULL AUTO_INCREMENT, `id` bigint(20) NOT NULL AUTO_INCREMENT,
`currency_code` varchar(3) NOT NULL, `currency_code` varchar(3) NOT NULL,
@@ -225,6 +238,7 @@ CREATE TABLE `portfolio_product_loan` (
`name` varchar(100) NOT NULL, `name` varchar(100) NOT NULL,
`description` varchar(500) DEFAULT NULL, `description` varchar(500) DEFAULT NULL,
`fund_id` bigint(20) DEFAULT NULL, `fund_id` bigint(20) DEFAULT NULL,
`loan_transaction_strategy_id` bigint(20) DEFAULT NULL,
`nominal_interest_rate_per_period` decimal(19,6) NOT NULL, `nominal_interest_rate_per_period` decimal(19,6) NOT NULL,
`interest_period_frequency_enum` smallint(5) NOT NULL, `interest_period_frequency_enum` smallint(5) NOT NULL,
`annual_nominal_interest_rate` decimal(19,6) NOT NULL, `annual_nominal_interest_rate` decimal(19,6) NOT NULL,
@@ -244,6 +258,8 @@ CREATE TABLE `portfolio_product_loan` (
KEY `FKAUD0000000000003` (`createdby_id`), KEY `FKAUD0000000000003` (`createdby_id`),
KEY `FKAUD0000000000004` (`lastmodifiedby_id`), KEY `FKAUD0000000000004` (`lastmodifiedby_id`),
KEY `FKA6A8A7D77240145` (`fund_id`), KEY `FKA6A8A7D77240145` (`fund_id`),
KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`),
CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`),
CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `org_fund` (`id`), CONSTRAINT `FKA6A8A7D77240145` FOREIGN KEY (`fund_id`) REFERENCES `org_fund` (`id`),
CONSTRAINT `FKAUD0000000000003` FOREIGN KEY (`createdby_id`) REFERENCES `admin_appuser` (`id`), CONSTRAINT `FKAUD0000000000003` FOREIGN KEY (`createdby_id`) REFERENCES `admin_appuser` (`id`),
CONSTRAINT `FKAUD0000000000004` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `admin_appuser` (`id`) CONSTRAINT `FKAUD0000000000004` FOREIGN KEY (`lastmodifiedby_id`) REFERENCES `admin_appuser` (`id`)
@@ -0,0 +1,21 @@
-- add loan transactions processing table
CREATE TABLE `ref_loan_transaction_processing_strategy` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`code` varchar(100) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`createdby_id` bigint(20) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ltp_strategy_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
ALTER TABLE `portfolio_product_loan`
ADD COLUMN `loan_transaction_strategy_id` bigint(20) DEFAULT NULL,
ADD KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`),
ADD CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`);
-- update ref table with out-of-the-box strategies
@@ -88,12 +88,14 @@ public class LoanProductsApiResource {
@Produces({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON})
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) { public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) {
// TODO - KW - every addition of a parameter here requires same additon in supported params used in 'apiDataConversionService' method.
Set<String> typicalResponseParameters = new HashSet<String>( Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "name", "description", "fundId", "fundName", "principal", "inArrearsTolerance", "numberOfRepayments", Arrays.asList("id", "name", "description", "fundId", "fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"createdOn", "lastModifedOn","currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "createdOn", "lastModifedOn","currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions") "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions")
); );
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
@@ -114,7 +116,9 @@ public class LoanProductsApiResource {
public String retrieveLoanProductDetails(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) { public String retrieveLoanProductDetails(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) {
Set<String> typicalResponseParameters = new HashSet<String>( Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "name", "description", "fundId", "fundName", "principal", "inArrearsTolerance", "numberOfRepayments", Arrays.asList("id", "name", "description", "fundId", "fundName",
"transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"createdOn", "lastModifedOn") "createdOn", "lastModifedOn")
@@ -130,7 +134,7 @@ public class LoanProductsApiResource {
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId); LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
if (template) { if (template) {
responseParameters.addAll(Arrays.asList("currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", responseParameters.addAll(Arrays.asList("currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions")); "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions"));
} }
return this.apiDataConversionService.convertLoanProductDataToJson(prettyPrint, responseParameters, loanProduct); return this.apiDataConversionService.convertLoanProductDataToJson(prettyPrint, responseParameters, loanProduct);
@@ -13,6 +13,7 @@ public class LoanProductCommand {
private final String description; private final String description;
private final Long fundId; private final Long fundId;
private final Long transactionProcessingStrategyId;
private final String currencyCode; private final String currencyCode;
private final Integer digitsAfterDecimal; private final Integer digitsAfterDecimal;
@@ -33,7 +34,7 @@ public class LoanProductCommand {
private final Set<String> modifiedParameters; private final Set<String> modifiedParameters;
public LoanProductCommand(final Set<String> modifiedParameters, final Long id, final String name, final String description, public LoanProductCommand(final Set<String> modifiedParameters, final Long id, final String name, final String description,
final Long fundId, final Long fundId, final Long transactionProcessingStrategyId,
final String currencyCode, final Integer digitsAfterDecimal, final String currencyCode, final Integer digitsAfterDecimal,
final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments, final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final Integer repaymentEvery, final BigDecimal interestRatePerPeriod,
@@ -44,6 +45,7 @@ public class LoanProductCommand {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.fundId = fundId; this.fundId = fundId;
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
this.digitsAfterDecimal = digitsAfterDecimal; this.digitsAfterDecimal = digitsAfterDecimal;
this.principal = principal; this.principal = principal;
@@ -73,6 +75,10 @@ public class LoanProductCommand {
public Long getFundId() { public Long getFundId() {
return fundId; return fundId;
} }
public Long getTransactionProcessingStrategyId() {
return transactionProcessingStrategyId;
}
public String getCurrencyCode() { public String getCurrencyCode() {
return currencyCode; return currencyCode;
@@ -133,6 +139,10 @@ public class LoanProductCommand {
public boolean isFundChanged() { public boolean isFundChanged() {
return this.modifiedParameters.contains("fundId"); return this.modifiedParameters.contains("fundId");
} }
public boolean isTransactionProcessingStrategyChanged() {
return this.modifiedParameters.contains("transactionProcessingStrategyId");
}
public boolean isCurrencyCodeChanged() { public boolean isCurrencyCodeChanged() {
return this.modifiedParameters.contains("currencyCode"); return this.modifiedParameters.contains("currencyCode");
@@ -1,6 +1,5 @@
package org.mifosng.platform.api.data; package org.mifosng.platform.api.data;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -8,7 +7,7 @@ import java.util.List;
import org.joda.time.DateTime; import org.joda.time.DateTime;
public class LoanProductData implements Serializable { public class LoanProductData {
private Long id; private Long id;
private String name; private String name;
@@ -16,6 +15,9 @@ public class LoanProductData implements Serializable {
private Long fundId; private Long fundId;
private String fundName; private String fundName;
private Long transactionProcessingStrategyId;
private String transactionProcessingStrategyName;
private MoneyData principal; private MoneyData principal;
private MoneyData inArrearsTolerance; private MoneyData inArrearsTolerance;
@@ -36,6 +38,7 @@ public class LoanProductData implements Serializable {
private List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>(); private List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>();
private Collection<FundData> fundOptions = new ArrayList<FundData>(); private Collection<FundData> fundOptions = new ArrayList<FundData>();
private Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = new ArrayList<TransactionProcessingStrategyData>();
private List<EnumOptionData> amortizationTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> amortizationTypeOptions = new ArrayList<EnumOptionData>();
private List<EnumOptionData> interestTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> interestTypeOptions = new ArrayList<EnumOptionData>();
private List<EnumOptionData> interestCalculationPeriodTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> interestCalculationPeriodTypeOptions = new ArrayList<EnumOptionData>();
@@ -55,7 +58,7 @@ public class LoanProductData implements Serializable {
EnumOptionData interestRateFrequencyType, EnumOptionData interestRateFrequencyType,
EnumOptionData amortizationType, EnumOptionData interestType, EnumOptionData amortizationType, EnumOptionData interestType,
EnumOptionData interestCalculationPeriodType, Long fundId, EnumOptionData interestCalculationPeriodType, Long fundId,
String fundName) { String fundName, Long transactionProcessingStrategyId, String transactionProcessingStrategyName) {
this.createdOn = createdOn; this.createdOn = createdOn;
this.lastModifedOn = lastModifedOn; this.lastModifedOn = lastModifedOn;
this.id = id; this.id = id;
@@ -74,6 +77,8 @@ public class LoanProductData implements Serializable {
this.interestCalculationPeriodType = interestCalculationPeriodType; this.interestCalculationPeriodType = interestCalculationPeriodType;
this.fundId = fundId; this.fundId = fundId;
this.fundName = fundName; this.fundName = fundName;
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
this.transactionProcessingStrategyName = transactionProcessingStrategyName;
} }
public Long getId() { public Long getId() {
@@ -115,6 +120,23 @@ public class LoanProductData implements Serializable {
public void setFundName(String fundName) { public void setFundName(String fundName) {
this.fundName = fundName; this.fundName = fundName;
} }
public Long getTransactionProcessingStrategyId() {
return transactionProcessingStrategyId;
}
public void setTransactionProcessingStrategyId(Long transactionProcessingStrategyId) {
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
}
public String getTransactionProcessingStrategyName() {
return transactionProcessingStrategyName;
}
public void setTransactionProcessingStrategyName(
String transactionProcessingStrategyName) {
this.transactionProcessingStrategyName = transactionProcessingStrategyName;
}
public MoneyData getPrincipal() { public MoneyData getPrincipal() {
return principal; return principal;
@@ -255,6 +277,15 @@ public class LoanProductData implements Serializable {
public void setFundOptions(Collection<FundData> fundOptions) { public void setFundOptions(Collection<FundData> fundOptions) {
this.fundOptions = fundOptions; this.fundOptions = fundOptions;
} }
public Collection<TransactionProcessingStrategyData> getTransactionProcessingStrategyOptions() {
return transactionProcessingStrategyOptions;
}
public void setTransactionProcessingStrategyOptions(
Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions) {
this.transactionProcessingStrategyOptions = transactionProcessingStrategyOptions;
}
public List<EnumOptionData> getAmortizationTypeOptions() { public List<EnumOptionData> getAmortizationTypeOptions() {
return amortizationTypeOptions; return amortizationTypeOptions;
@@ -0,0 +1,27 @@
package org.mifosng.platform.api.data;
public final class TransactionProcessingStrategyData {
private final Long id;
private final String code;
private final String name;
public TransactionProcessingStrategyData(final Long id, final String code, final String name) {
this.id = id;
this.code = code;
this.name = name;
}
public Long getId() {
return id;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
@@ -226,12 +226,13 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
@Override @Override
public String convertLoanProductDataToJson(final boolean prettyPrint, final Set<String> responseParameters, final LoanProductData... products) { public String convertLoanProductDataToJson(final boolean prettyPrint, final Set<String> responseParameters, final LoanProductData... products) {
Set<String> supportedParameters = new HashSet<String>( Set<String> supportedParameters = new HashSet<String>(
Arrays.asList("id", "name", "description", "fundId", "fundName", "principal", "inArrearsTolerance", "numberOfRepayments", Arrays.asList("id", "name", "description", "fundId", "fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"createdOn", "lastModifedOn", "createdOn", "lastModifedOn",
"currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions") "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions")
); );
final Set<String> parameterNamesToSkip = new HashSet<String>(); final Set<String> parameterNamesToSkip = new HashSet<String>();
@@ -827,7 +828,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap); Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>( Set<String> supportedParams = new HashSet<String>(
Arrays.asList("name", "description", "fundId", "currencyCode", "digitsAfterDecimal", Arrays.asList("name", "description", "fundId", "transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal",
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "locale") "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "locale")
); );
@@ -839,6 +840,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
String name = extractStringParameter("name", requestMap, modifiedParameters); String name = extractStringParameter("name", requestMap, modifiedParameters);
String description = extractStringParameter("description", requestMap, modifiedParameters); String description = extractStringParameter("description", requestMap, modifiedParameters);
Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters); Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters);
Long transactionProcessingStrategyId = extractLongParameter("transactionProcessingStrategyId", requestMap, modifiedParameters);
String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters); String currencyCode = extractStringParameter("currencyCode", requestMap, modifiedParameters);
Integer digitsAfterDecimalValue = extractIntegerParameter("digitsAfterDecimal", requestMap, modifiedParameters); Integer digitsAfterDecimalValue = extractIntegerParameter("digitsAfterDecimal", requestMap, modifiedParameters);
BigDecimal principalValue = extractBigDecimalParameter("principal", requestMap, modifiedParameters); BigDecimal principalValue = extractBigDecimalParameter("principal", requestMap, modifiedParameters);
@@ -854,7 +856,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters); Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters);
Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters); Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters);
return new LoanProductCommand(modifiedParameters, resourceIdentifier, name, description, fundId, return new LoanProductCommand(modifiedParameters, resourceIdentifier, name, description, fundId, transactionProcessingStrategyId,
currencyCode, digitsAfterDecimalValue, principalValue, inArrearsToleranceValue, numberOfRepaymentsValue, repaymentEveryValue, interestRatePerPeriodValue, currencyCode, digitsAfterDecimalValue, principalValue, inArrearsToleranceValue, numberOfRepaymentsValue, repaymentEveryValue, interestRatePerPeriodValue,
repaymentFrequencyTypeValue, interestRateFrequencyTypeValue, amortizationTypeValue, interestTypeValue, interestCalculationPeriodTypeValue); repaymentFrequencyTypeValue, interestRateFrequencyTypeValue, amortizationTypeValue, interestTypeValue, interestCalculationPeriodTypeValue);
} }
@@ -0,0 +1,11 @@
package org.mifosng.platform.exceptions;
/**
* A {@link RuntimeException} thrown when loan transactions processing strategy resources are not found.
*/
public class LoanTransactionProcessingStrategyNotFoundException extends AbstractPlatformResourceNotFoundException {
public LoanTransactionProcessingStrategyNotFoundException(final Long id) {
super("error.msg.transactions.processing.strategy.id.invalid", "Loan transaction processing strategy with identifier " + id + " does not exist", id);
}
}
@@ -32,6 +32,10 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "fund_id", nullable = true) @JoinColumn(name = "fund_id", nullable = true)
private Fund fund; private Fund fund;
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false) @Column(name = "name", nullable = false)
private String name; private String name;
@@ -49,12 +53,14 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
this.loanProductRelatedDetail = null; this.loanProductRelatedDetail = null;
} }
public LoanProduct(final Fund fund, final String name, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal, public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final String name, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate, final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments, final AmortizationMethod amortizationMethod, final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments, final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance) { final BigDecimal inArrearsTolerance) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim(); this.name = name.trim();
if (StringUtils.isNotBlank(description)) { if (StringUtils.isNotBlank(description)) {
this.description = description.trim(); this.description = description.trim();
@@ -96,10 +102,6 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProductRelatedDetail.isInterestRebateAllowed(); return this.loanProductRelatedDetail.isInterestRebateAllowed();
} }
public boolean identifiedBy(final String identifier) {
return identifier.equalsIgnoreCase(this.name);
}
public BigDecimal getDefaultNominalInterestRatePerPeriod() { public BigDecimal getDefaultNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod(); return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
} }
@@ -131,8 +133,12 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
public Fund getFund() { public Fund getFund() {
return fund; return fund;
} }
public LoanTransactionProcessingStrategy getTransactionProcessingStrategy() {
return transactionProcessingStrategy;
}
public void update(final LoanProductCommand command, final Fund fund) { public void update(final LoanProductCommand command, final Fund fund, final LoanTransactionProcessingStrategy strategy) {
if (command.isNameChanged()) { if (command.isNameChanged()) {
this.name = command.getName(); this.name = command.getName();
@@ -146,6 +152,10 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
this.fund = fund; this.fund = fund;
} }
if (command.isTransactionProcessingStrategyChanged()) {
this.transactionProcessingStrategy = strategy;
}
this.loanProductRelatedDetail.update(command); this.loanProductRelatedDetail.update(command);
} }
} }
@@ -0,0 +1,35 @@
package org.mifosng.platform.loan.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.user.domain.AppUser;
/**
*
*/
@Entity
@Table(name = "ref_loan_transaction_processing_strategy")
public class LoanTransactionProcessingStrategy extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "code", unique=true)
private String code;
@Column(name = "name")
private String name;
protected LoanTransactionProcessingStrategy() {
//
}
public String code() {
return this.code;
}
public TransactionProcessingStrategyData toData() {
return new TransactionProcessingStrategyData(this.getId(), this.code, this.name);
}
}
@@ -0,0 +1,9 @@
package org.mifosng.platform.loan.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface LoanTransactionProcessingStrategyRepository extends JpaRepository<LoanTransactionProcessingStrategy, Long>,
JpaSpecificationExecutor<LoanTransactionProcessingStrategy> {
// no added behaviour
}
@@ -1,8 +1,10 @@
package org.mifosng.platform.loanproduct.service; package org.mifosng.platform.loanproduct.service;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.mifosng.platform.api.data.EnumOptionData; import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
public interface LoanDropdownReadPlatformService { public interface LoanDropdownReadPlatformService {
@@ -15,4 +17,6 @@ public interface LoanDropdownReadPlatformService {
List<EnumOptionData> retrieveRepaymentFrequencyTypeOptions(); List<EnumOptionData> retrieveRepaymentFrequencyTypeOptions();
List<EnumOptionData> retrieveInterestRateFrequencyTypeOptions(); List<EnumOptionData> retrieveInterestRateFrequencyTypeOptions();
Collection<TransactionProcessingStrategyData> retreiveTransactionProcessingStrategies();
} }
@@ -6,20 +6,32 @@ import static org.mifosng.platform.loanproduct.service.LoanEnumerations.interest
import static org.mifosng.platform.loanproduct.service.LoanEnumerations.interestType; import static org.mifosng.platform.loanproduct.service.LoanEnumerations.interestType;
import static org.mifosng.platform.loanproduct.service.LoanEnumerations.repaymentFrequencyType; import static org.mifosng.platform.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.mifosng.platform.api.data.EnumOptionData; import org.mifosng.platform.api.data.EnumOptionData;
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod; import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod; import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategy;
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
public class LoanDropdownReadPlatformServiceImpl implements public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlatformService {
LoanDropdownReadPlatformService {
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
@Autowired
public LoanDropdownReadPlatformServiceImpl(final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) {
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
}
@Override @Override
public List<EnumOptionData> retrieveLoanAmortizationTypeOptions() { public List<EnumOptionData> retrieveLoanAmortizationTypeOptions() {
@@ -67,4 +79,17 @@ public class LoanDropdownReadPlatformServiceImpl implements
interestRateFrequencyType(PeriodFrequencyType.YEARS)); interestRateFrequencyType(PeriodFrequencyType.YEARS));
return interestRateFrequencyTypeOptions; return interestRateFrequencyTypeOptions;
} }
@Override
public Collection<TransactionProcessingStrategyData> retreiveTransactionProcessingStrategies() {
Collection<TransactionProcessingStrategyData> strategyOptions = new ArrayList<TransactionProcessingStrategyData>();
List<LoanTransactionProcessingStrategy> strategies = this.loanTransactionProcessingStrategyRepository.findAll();
for (LoanTransactionProcessingStrategy strategy : strategies) {
strategyOptions.add(strategy.toData());
}
return strategyOptions;
}
} }
@@ -13,6 +13,7 @@ import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.data.LoanProductData; import org.mifosng.platform.api.data.LoanProductData;
import org.mifosng.platform.api.data.LoanProductLookup; import org.mifosng.platform.api.data.LoanProductLookup;
import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.api.data.MoneyData;
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
import org.mifosng.platform.currency.service.CurrencyReadPlatformService; import org.mifosng.platform.currency.service.CurrencyReadPlatformService;
import org.mifosng.platform.exceptions.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException;
import org.mifosng.platform.fund.service.FundReadPlatformService; import org.mifosng.platform.fund.service.FundReadPlatformService;
@@ -128,14 +129,12 @@ public class LoanProductReadPlatformServiceImpl implements
private static final class LoanProductMapper implements private static final class LoanProductMapper implements
RowMapper<LoanProductData> { RowMapper<LoanProductData> {
// private final List<CurrencyData> allowedCurrencies;
// private final Collection<FundData> allFunds;
public LoanProductMapper() { public LoanProductMapper() {
} }
public String loanProductSchema() { public String loanProductSchema() {
return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.name as name, lp.description as description, lp.flexible_repayment_schedule as isFlexible, lp.interest_rebate as isInterestRebateAllowed, " return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, "
+ "lp.name as name, lp.description as description, lp.flexible_repayment_schedule as isFlexible, lp.interest_rebate as isInterestRebateAllowed, "
+ "lp.principal_amount as principal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, " + "lp.principal_amount as principal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, "
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod," + "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,"
@@ -145,6 +144,7 @@ public class LoanProductReadPlatformServiceImpl implements
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol " + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol "
+ " from portfolio_product_loan lp " + " from portfolio_product_loan lp "
+ " left join org_fund f on f.id = lp.fund_id" + " left join org_fund f on f.id = lp.fund_id"
+ " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id"
+ " join ref_currency curr on curr.code = lp.currency_code"; + " join ref_currency curr on curr.code = lp.currency_code";
} }
@@ -158,6 +158,9 @@ public class LoanProductReadPlatformServiceImpl implements
Long fundId = JdbcSupport.getLong(rs, "fundId"); Long fundId = JdbcSupport.getLong(rs, "fundId");
String fundName = rs.getString("fundName"); String fundName = rs.getString("fundName");
Long transactionStrategyId = JdbcSupport.getLong(rs, "transactionStrategyId");
String transactionStrategyName = rs.getString("transactionStrategyName");
String currencyCode = rs.getString("currencyCode"); String currencyCode = rs.getString("currencyCode");
String currencyName = rs.getString("currencyName"); String currencyName = rs.getString("currencyName");
@@ -217,7 +220,7 @@ public class LoanProductReadPlatformServiceImpl implements
numberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, repaymentEvery, interestRatePerPeriod,
annualInterestRate, repaymentFrequencyType, annualInterestRate, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType, fundId, fundName); interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName);
} }
} }
@@ -241,35 +244,26 @@ public class LoanProductReadPlatformServiceImpl implements
} }
private void populateProductDataWithDropdownOptions( private void populateProductDataWithDropdownOptions(final LoanProductData productData) {
LoanProductData productData) {
List<CurrencyData> currencyOptions = currencyReadPlatformService List<CurrencyData> currencyOptions = currencyReadPlatformService.retrieveAllowedCurrencies();
.retrieveAllowedCurrencies(); List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
List<EnumOptionData> amortizationTypeOptions = dropdownReadPlatformService List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
.retrieveLoanAmortizationTypeOptions(); List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
.retrieveLoanInterestTypeOptions(); List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService
.retrieveLoanInterestRateCalculatedInPeriodOptions();
List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService
.retrieveRepaymentFrequencyTypeOptions();
List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
.retrieveAllFunds(); Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
productData.setCurrencyOptions(currencyOptions); productData.setCurrencyOptions(currencyOptions);
productData.setAmortizationTypeOptions(amortizationTypeOptions); productData.setAmortizationTypeOptions(amortizationTypeOptions);
productData.setInterestTypeOptions(interestTypeOptions); productData.setInterestTypeOptions(interestTypeOptions);
productData productData.setInterestCalculationPeriodTypeOptions(interestCalculationPeriodTypeOptions);
.setInterestCalculationPeriodTypeOptions(interestCalculationPeriodTypeOptions); productData.setRepaymentFrequencyTypeOptions(repaymentFrequencyTypeOptions);
productData productData.setInterestRateFrequencyTypeOptions(interestRateFrequencyTypeOptions);
.setRepaymentFrequencyTypeOptions(repaymentFrequencyTypeOptions);
productData
.setInterestRateFrequencyTypeOptions(interestRateFrequencyTypeOptions);
productData.setFundOptions(fundOptions); productData.setFundOptions(fundOptions);
productData.setTransactionProcessingStrategyOptions(transactionProcessingStrategyOptions);
} }
} }
@@ -7,6 +7,7 @@ import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.exceptions.FundNotFoundException; import org.mifosng.platform.exceptions.FundNotFoundException;
import org.mifosng.platform.exceptions.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException;
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosng.platform.fund.domain.Fund; import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.fund.domain.FundRepository; import org.mifosng.platform.fund.domain.FundRepository;
import org.mifosng.platform.loan.domain.AmortizationMethod; import org.mifosng.platform.loan.domain.AmortizationMethod;
@@ -14,6 +15,8 @@ import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod; import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.LoanProduct; import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRepository; import org.mifosng.platform.loan.domain.LoanProductRepository;
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategy;
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType; import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanschedule.domain.AprCalculator; import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.security.PlatformSecurityContext;
@@ -28,14 +31,16 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
@Autowired @Autowired
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanProductRepository loanProductRepository, public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository) { final AprCalculator aprCalculator, final FundRepository fundRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) {
this.context = context; this.context = context;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
this.aprCalculator = aprCalculator; this.aprCalculator = aprCalculator;
this.fundRepository = fundRepository; this.fundRepository = fundRepository;
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
} }
@Transactional @Transactional
@@ -65,8 +70,10 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
// associating fund with loan product at creation is optional for now. // associating fund with loan product at creation is optional for now.
Fund fund = findFundByIdIfProvided(command.getFundId()); Fund fund = findFundByIdIfProvided(command.getFundId());
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
LoanProduct loanproduct = new LoanProduct(fund, command.getName(), command.getDescription(), LoanProduct loanproduct = new LoanProduct(fund, loanTransactionProcessingStrategy,
command.getName(), command.getDescription(),
currency, command.getPrincipal(), currency, command.getPrincipal(),
command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, command.getInterestRatePerPeriod(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
command.getRepaymentEvery(), repaymentFrequencyType, command.getNumberOfRepayments(), command.getRepaymentEvery(), repaymentFrequencyType, command.getNumberOfRepayments(),
@@ -77,6 +84,17 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
return new EntityIdentifier(loanproduct.getId()); return new EntityIdentifier(loanproduct.getId());
} }
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
LoanTransactionProcessingStrategy fund = null;
if (transactionProcessingStrategyId != null) {
fund = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
if (fund == null) {
throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId);
}
}
return fund;
}
private Fund findFundByIdIfProvided(final Long fundId) { private Fund findFundByIdIfProvided(final Long fundId) {
Fund fund = null; Fund fund = null;
if (fundId != null) { if (fundId != null) {
@@ -104,8 +122,9 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
// associating fund with loan product at creation is optional for now. // associating fund with loan product at creation is optional for now.
Fund fund = findFundByIdIfProvided(command.getFundId()); Fund fund = findFundByIdIfProvided(command.getFundId());
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
product.update(command, fund); product.update(command, fund, loanTransactionProcessingStrategy);
this.loanProductRepository.save(product); this.loanProductRepository.save(product);