support basic operations on fund concept and associating as optional with loan product.

This commit is contained in:
Keith Woodlock
2012-07-11 09:37:34 +01:00
parent 21d5a4cc1a
commit 855adc27ef
12 changed files with 142 additions and 140 deletions
@@ -9,7 +9,6 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@@ -18,7 +17,6 @@ import javax.ws.rs.core.UriInfo;
import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.FundData; import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.api.infrastructure.ApiJSONFormattingService; import org.mifosng.platform.api.infrastructure.ApiJSONFormattingService;
import org.mifosng.platform.fund.service.FundReadPlatformService; import org.mifosng.platform.fund.service.FundReadPlatformService;
import org.mifosng.platform.fund.service.FundWritePlatformService; import org.mifosng.platform.fund.service.FundWritePlatformService;
@@ -31,7 +29,6 @@ import org.springframework.stereotype.Component;
@Scope("singleton") @Scope("singleton")
public class FundsApiResource { public class FundsApiResource {
private String defaultFieldList = "openingDate";
private String allowedFieldList = "allowedParents"; private String allowedFieldList = "allowedParents";
private String filterName = "myFilter"; private String filterName = "myFilter";
@@ -41,9 +38,6 @@ public class FundsApiResource {
@Autowired @Autowired
private FundWritePlatformService writePlatformService; private FundWritePlatformService writePlatformService;
@Autowired
private ApiDataConversionService apiDataConversionService;
@Autowired @Autowired
private ApiJSONFormattingService jsonFormattingService; private ApiJSONFormattingService jsonFormattingService;
@@ -63,11 +57,6 @@ public class FundsApiResource {
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response createFund(final FundCommand command) { public Response createFund(final FundCommand command) {
// LocalDate openingLocalDate = apiDataConversionService.convertFrom(
// command.getOpeningDate(), "openingDate",
// command.getDateFormat());
// command.setOpeningLocalDate(openingLocalDate);
Long fundId = this.writePlatformService.createFund(command); Long fundId = this.writePlatformService.createFund(command);
return Response.ok().entity(new EntityIdentifier(fundId)).build(); return Response.ok().entity(new EntityIdentifier(fundId)).build();
@@ -77,14 +66,9 @@ public class FundsApiResource {
@Path("{fundId}") @Path("{fundId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retreiveOffice(@PathParam("fundId") final Long fundId, public String retreiveOffice(@PathParam("fundId") final Long fundId, @Context UriInfo uriInfo) {
@QueryParam("template") String template, @Context UriInfo uriInfo) {
FundData fund = this.readPlatformService.retrieveFund(fundId); FundData fund = this.readPlatformService.retrieveFund(fundId);
// if (template != null && template.equalsIgnoreCase("true")) {
// office.setAllowedParents(this.readPlatformService
// .retrieveAllowedParents(officeId));
// }
String selectedFields = ""; String selectedFields = "";
return this.jsonFormattingService.convertRequest(fund, filterName, return this.jsonFormattingService.convertRequest(fund, filterName,
@@ -95,13 +79,8 @@ public class FundsApiResource {
@Path("{fundId}") @Path("{fundId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public Response updateOffice(@PathParam("fundId") final Long fundId, public Response updateFund(@PathParam("fundId") final Long fundId, final FundCommand command) {
final FundCommand command) {
// LocalDate openingLocalDate = apiDataConversionService.convertFrom(
// command.getOpeningDate(), "openingDate",
// command.getDateFormat());
// command.setOpeningLocalDate(openingLocalDate);
command.setId(fundId); command.setId(fundId);
Long entityId = this.writePlatformService.updateFund(command); Long entityId = this.writePlatformService.updateFund(command);
@@ -2,8 +2,6 @@ package org.mifosng.platform.api.commands;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import org.joda.time.LocalDate;
/** /**
* Command used for create and update fund operations. * Command used for create and update fund operations.
*/ */
@@ -14,13 +12,6 @@ public class FundCommand {
private String name; private String name;
private String externalId; private String externalId;
private String dateFormat;
private String activatedOnDate;
private LocalDate activatedOnLocalDate;
private String deactivatedOnDate;
private LocalDate deactivatedOnLocalDate;
protected FundCommand() { protected FundCommand() {
// //
} }
@@ -53,44 +44,4 @@ public class FundCommand {
public void setExternalId(String externalId) { public void setExternalId(String externalId) {
this.externalId = externalId; this.externalId = externalId;
} }
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public String getActivatedOnDate() {
return activatedOnDate;
}
public void setActivatedOnDate(String activatedOnDate) {
this.activatedOnDate = activatedOnDate;
}
public LocalDate getActivatedOnLocalDate() {
return activatedOnLocalDate;
}
public void setActivatedOnLocalDate(LocalDate activatedOnLocalDate) {
this.activatedOnLocalDate = activatedOnLocalDate;
}
public String getDeactivatedOnDate() {
return deactivatedOnDate;
}
public void setDeactivatedOnDate(String deactivatedOnDate) {
this.deactivatedOnDate = deactivatedOnDate;
}
public LocalDate getDeactivatedOnLocalDate() {
return deactivatedOnLocalDate;
}
public void setDeactivatedOnLocalDate(LocalDate deactivatedOnLocalDate) {
this.deactivatedOnLocalDate = deactivatedOnLocalDate;
}
} }
@@ -12,6 +12,8 @@ public class LoanProductCommand {
private String description; private String description;
private String externalId; private String externalId;
private Long fundId;
private String locale; private String locale;
private String currencyCode; private String currencyCode;
private String digitsAfterDecimal; private String digitsAfterDecimal;
@@ -223,4 +225,12 @@ public class LoanProductCommand {
Integer interestCalculationPeriodType) { Integer interestCalculationPeriodType) {
this.interestCalculationPeriodType = interestCalculationPeriodType; this.interestCalculationPeriodType = interestCalculationPeriodType;
} }
public Long getFundId() {
return fundId;
}
public void setFundId(Long fundId) {
this.fundId = fundId;
}
} }
@@ -3,6 +3,7 @@ package org.mifosng.platform.api.data;
import java.io.Serializable; 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.List; import java.util.List;
import org.codehaus.jackson.map.annotate.JsonFilter; import org.codehaus.jackson.map.annotate.JsonFilter;
@@ -15,6 +16,8 @@ public class LoanProductData implements Serializable {
private String name; private String name;
private String description; private String description;
private FundData fund;
private MoneyData principal; private MoneyData principal;
private MoneyData inArrearsTolerance; private MoneyData inArrearsTolerance;
@@ -33,12 +36,14 @@ public class LoanProductData implements Serializable {
private DateTime lastModifedOn; private DateTime lastModifedOn;
private List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>(); private List<CurrencyData> currencyOptions = new ArrayList<CurrencyData>();
private Collection<FundData> fundOptions = new ArrayList<FundData>();
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>();
private List<EnumOptionData> repaymentFrequencyTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> repaymentFrequencyTypeOptions = new ArrayList<EnumOptionData>();
private List<EnumOptionData> interestRateFrequencyTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> interestRateFrequencyTypeOptions = new ArrayList<EnumOptionData>();
public LoanProductData() { public LoanProductData() {
// //
} }
@@ -52,7 +57,7 @@ public class LoanProductData implements Serializable {
EnumOptionData interestRateFrequencyType, EnumOptionData interestRateFrequencyType,
EnumOptionData amortizationType, EnumOptionData amortizationType,
EnumOptionData interestType, EnumOptionData interestType,
EnumOptionData interestCalculationPeriodType) { EnumOptionData interestCalculationPeriodType, FundData fund) {
this.createdOn = createdOn; this.createdOn = createdOn;
this.lastModifedOn = lastModifedOn; this.lastModifedOn = lastModifedOn;
this.id = id; this.id = id;
@@ -69,6 +74,7 @@ public class LoanProductData implements Serializable {
this.amortizationType = amortizationType; this.amortizationType = amortizationType;
this.interestType = interestType; this.interestType = interestType;
this.interestCalculationPeriodType = interestCalculationPeriodType; this.interestCalculationPeriodType = interestCalculationPeriodType;
this.fund = fund;
} }
public Long getId() { public Long getId() {
@@ -95,6 +101,14 @@ public class LoanProductData implements Serializable {
this.description = description; this.description = description;
} }
public FundData getFund() {
return fund;
}
public void setFund(FundData fund) {
this.fund = fund;
}
public MoneyData getPrincipal() { public MoneyData getPrincipal() {
return principal; return principal;
} }
@@ -227,6 +241,14 @@ public class LoanProductData implements Serializable {
this.currencyOptions = currencyOptions; this.currencyOptions = currencyOptions;
} }
public Collection<FundData> getFundOptions() {
return fundOptions;
}
public void setFundOptions(Collection<FundData> fundOptions) {
this.fundOptions = fundOptions;
}
public List<EnumOptionData> getAmortizationTypeOptions() { public List<EnumOptionData> getAmortizationTypeOptions() {
return amortizationTypeOptions; return amortizationTypeOptions;
} }
@@ -1,15 +1,11 @@
package org.mifosng.platform.fund.domain; package org.mifosng.platform.fund.domain;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.UniqueConstraint;
import javax.persistence.TemporalType;
import org.mifosng.platform.api.commands.FundCommand; import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
@@ -17,7 +13,10 @@ import org.mifosng.platform.organisation.domain.Organisation;
import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUser;
@Entity @Entity
@Table(name = "org_fund") @Table(name = "org_fund", uniqueConstraints={
@UniqueConstraint(columnNames = {"org_id", "name"}, name="fund_name_org"),
@UniqueConstraint(columnNames = {"org_id", "external_id"}, name="fund_externalid_org")
})
public class Fund extends AbstractAuditableCustom<AppUser, Long> { public class Fund extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne @ManyToOne
@@ -30,17 +29,6 @@ public class Fund extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "external_id", length=100) @Column(name = "external_id", length=100)
private String externalId; private String externalId;
@Temporal(TemporalType.DATE)
@Column(name = "activatedon_date")
private Date activatedOnDate;
@Temporal(TemporalType.DATE)
@Column(name = "deactivatedon_date")
private Date deactivatedOnDate;
@Column(name = "initial_balance", scale = 6, precision = 19)
private BigDecimal initialBalance;
public static Fund createNew(final Organisation organisation, final String fundName) { public static Fund createNew(final Organisation organisation, final String fundName) {
return new Fund(organisation, fundName); return new Fund(organisation, fundName);
} }
@@ -52,7 +40,6 @@ public class Fund extends AbstractAuditableCustom<AppUser, Long> {
private Fund(final Organisation organisation, final String fundName) { private Fund(final Organisation organisation, final String fundName) {
this.organisation = organisation; this.organisation = organisation;
this.name = fundName; this.name = fundName;
this.initialBalance = BigDecimal.ZERO;
} }
public void update(FundCommand command) { public void update(FundCommand command) {
@@ -22,11 +22,9 @@ public class FundCommandValidator {
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("fund"); DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("fund");
// baseDataValidator.reset().parameter("name").value(command.getName()).notBlank(); baseDataValidator.reset().parameter("name").value(command.getName()).notBlank();
// baseDataValidator.reset().parameter("openingDate").value(command.getOpeningDate()).notBlank(); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).notExceedingLengthOf(100);
// baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).notExceedingLengthOf(100);
// baseDataValidator.reset().parameter("parentId").value(command.getParentId()).notNull().greaterThanZero();
//
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
} }
@@ -36,15 +34,13 @@ public class FundCommandValidator {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("fund"); DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("fund");
//
// baseDataValidator.reset().parameter("id").value(command.getId()).notNull(); baseDataValidator.reset().parameter("id").value(command.getId()).notNull();
// baseDataValidator.reset().parameter("name").value(command.getName()).ignoreIfNull().notBlank(); baseDataValidator.reset().parameter("name").value(command.getName()).ignoreIfNull().notBlank();
// baseDataValidator.reset().parameter("openingDate").value(command.getOpeningDate()).ignoreIfNull().notBlank(); baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100);
// baseDataValidator.reset().parameter("externalId").value(command.getExternalId()).ignoreIfNull().notExceedingLengthOf(100);
// baseDataValidator.reset().parameter("parentId").value(command.getParentId()).ignoreIfNull().notNull().greaterThanZero(); baseDataValidator.reset().anyOfNotNull(command.getName(), command.getExternalId());
//
// baseDataValidator.reset().anyOfNotNull(command.getName(), command.getOpeningDate(), command.getExternalId(), command.getParentId());
//
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
} }
@@ -42,7 +42,6 @@ public class FundReadPlatformServiceImpl implements FundReadPlatformService {
Long id = rs.getLong("id"); Long id = rs.getLong("id");
String name = rs.getString("name"); String name = rs.getString("name");
String externalId = rs.getString("externalId"); String externalId = rs.getString("externalId");
// LocalDate openingDate = JdbcSupport.getLocalDate(rs, "openingDate");
return new FundData(id, name, externalId); return new FundData(id, name, externalId);
} }
@@ -83,13 +83,13 @@ public class FundWritePlatformServiceJpaRepositoryImpl implements FundWritePlatf
private void handleFundDataIntegrityIssues(final FundCommand command, DataIntegrityViolationException dve) { private void handleFundDataIntegrityIssues(final FundCommand command, DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause(); Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("externalid_org")) { if (realCause.getMessage().contains("fund_externalid_org")) {
throw new PlatformDataIntegrityException("error.msg.office.duplicate.externalId", "Office with externalId {0} already exists", "externalId", command.getExternalId()); throw new PlatformDataIntegrityException("error.msg.fund.duplicate.externalId", "Fund with externalId {0} already exists", "externalId", command.getExternalId());
} else if (realCause.getMessage().contains("name_org")) { } else if (realCause.getMessage().contains("fund_name_org")) {
throw new PlatformDataIntegrityException("error.msg.office.duplicate.name", "Office with name {0} already exists", "name", command.getName()); throw new PlatformDataIntegrityException("error.msg.fund.duplicate.name", "Fund with name {0} already exists", "name", command.getName());
} }
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.office.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); throw new PlatformDataIntegrityException("error.msg.fund.unknown.data.integrity.issue", "Unknown data integrity issue with resource: " + realCause.getMessage());
} }
} }
@@ -13,6 +13,7 @@ import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money; import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.organisation.domain.Organisation; import org.mifosng.platform.organisation.domain.Organisation;
import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUser;
@@ -29,32 +30,38 @@ import org.mifosng.platform.user.domain.AppUser;
@Table(name = "portfolio_product_loan") @Table(name = "portfolio_product_loan")
public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> { public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "org_id", nullable = false) @JoinColumn(name = "org_id", nullable = false)
private final Organisation organisation; private final Organisation organisation;
@Column(name = "name", nullable = false) @ManyToOne
private String name; @JoinColumn(name = "fund_id", nullable = true)
private Fund fund;
@Column(name = "description") @Column(name = "name", nullable = false)
private String description; private String name;
@Embedded @Column(name = "description")
private final LoanProductRelatedDetail loanProductRelatedDetail; private String description;
@Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail;
public LoanProduct() { public LoanProduct() {
this.organisation = null; this.organisation = null;
this.fund = null;
this.name = null; this.name = null;
this.description = null; this.description = null;
this.loanProductRelatedDetail = null; this.loanProductRelatedDetail = null;
} }
public LoanProduct(final Organisation organisation, final String name, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal, public LoanProduct(final Organisation organisation, Fund fund, 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.organisation = organisation; this.organisation = organisation;
this.fund = fund;
this.name = name.trim(); this.name = name.trim();
if (StringUtils.isNotBlank(description)) { if (StringUtils.isNotBlank(description)) {
this.description = description.trim(); this.description = description.trim();
@@ -132,13 +139,17 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProductRelatedDetail.getAmortizationMethod(); return this.loanProductRelatedDetail.getAmortizationMethod();
} }
public void update(LoanProductCommand command) { public void update(final LoanProductCommand command, final Fund fund) {
if (command.getName() != null) { if (command.getName() != null) {
this.name = command.getName(); this.name = command.getName();
} }
if (command.getDescription() != null) { if (command.getDescription() != null) {
this.description = command.getDescription(); this.description = command.getDescription();
} }
if (fund != null) {
this.fund = fund;
}
this.loanProductRelatedDetail.update(command); this.loanProductRelatedDetail.update(command);
} }
} }
@@ -44,7 +44,7 @@ public class LoanProductCommandValidator {
baseDataValidator.reset().parameter("interestType").value(command.getInterestType()).ignoreIfNull().notNull().inMinMaxRange(0, 1); baseDataValidator.reset().parameter("interestType").value(command.getInterestType()).ignoreIfNull().notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(command.getInterestCalculationPeriodType()).ignoreIfNull().notNull().inMinMaxRange(0, 1); baseDataValidator.reset().parameter("interestCalculationPeriodType").value(command.getInterestCalculationPeriodType()).ignoreIfNull().notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().anyOfNotNull(command.getName(), command.getDescription(), command.getExternalId(), command.getCurrencyCode(), command.getDigitsAfterDecimal(), baseDataValidator.reset().anyOfNotNull(command.getFundId(), command.getName(), command.getDescription(), command.getExternalId(), command.getCurrencyCode(), command.getDigitsAfterDecimal(),
command.getPrincipal(), command.getInArrearsTolerance(), command.getRepaymentFrequencyType(), command.getRepaymentEvery(), command.getNumberOfRepayments(), command.getPrincipal(), command.getInArrearsTolerance(), command.getRepaymentFrequencyType(), command.getRepaymentEvery(), command.getNumberOfRepayments(),
command.getInterestRatePerPeriod(), command.getInterestRateFrequencyType(), command.getAmortizationType(), command.getInterestType(), command.getInterestCalculationPeriodType()); command.getInterestRatePerPeriod(), command.getInterestRateFrequencyType(), command.getAmortizationType(), command.getInterestType(), command.getInterestCalculationPeriodType());
@@ -11,11 +11,13 @@ import javax.sql.DataSource;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.mifosng.platform.api.data.CurrencyData; import org.mifosng.platform.api.data.CurrencyData;
import org.mifosng.platform.api.data.EnumOptionData; import org.mifosng.platform.api.data.EnumOptionData;
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.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.infrastructure.JdbcSupport; import org.mifosng.platform.infrastructure.JdbcSupport;
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;
@@ -37,15 +39,18 @@ public class LoanProductReadPlatformServiceImpl implements
private final CurrencyReadPlatformService currencyReadPlatformService; private final CurrencyReadPlatformService currencyReadPlatformService;
private final SimpleJdbcTemplate jdbcTemplate; private final SimpleJdbcTemplate jdbcTemplate;
private final LoanDropdownReadPlatformService dropdownReadPlatformService; private final LoanDropdownReadPlatformService dropdownReadPlatformService;
private final FundReadPlatformService fundReadPlatformService;
@Autowired @Autowired
public LoanProductReadPlatformServiceImpl( public LoanProductReadPlatformServiceImpl(
final PlatformSecurityContext context, final PlatformSecurityContext context,
final CurrencyReadPlatformService currencyReadPlatformService, final CurrencyReadPlatformService currencyReadPlatformService,
final FundReadPlatformService fundReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final LoanDropdownReadPlatformService dropdownReadPlatformService,
final DataSource dataSource) { final DataSource dataSource) {
this.context = context; this.context = context;
this.currencyReadPlatformService = currencyReadPlatformService; this.currencyReadPlatformService = currencyReadPlatformService;
this.fundReadPlatformService = fundReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService;
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource); this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
} }
@@ -57,7 +62,9 @@ public class LoanProductReadPlatformServiceImpl implements
List<CurrencyData> allowedCurrencies = currencyReadPlatformService List<CurrencyData> allowedCurrencies = currencyReadPlatformService
.retrieveAllPlatformCurrencies(); .retrieveAllPlatformCurrencies();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies); Collection<FundData> allFunds = this.fundReadPlatformService.retrieveAllFunds();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies, allFunds);
String sql = "select " + rm.loanProductSchema() String sql = "select " + rm.loanProductSchema()
+ " where lp.id = ?"; + " where lp.id = ?";
@@ -76,11 +83,12 @@ public class LoanProductReadPlatformServiceImpl implements
public Collection<LoanProductData> retrieveAllLoanProducts() { public Collection<LoanProductData> retrieveAllLoanProducts() {
AppUser currentUser = this.context.authenticatedUser(); AppUser currentUser = this.context.authenticatedUser();
// TODO - include currency read in the sql
List<CurrencyData> allowedCurrencies = currencyReadPlatformService
.retrieveAllPlatformCurrencies();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies); // TODO - include currency read in the sql
List<CurrencyData> allowedCurrencies = currencyReadPlatformService.retrieveAllPlatformCurrencies();
Collection<FundData> allFunds = this.fundReadPlatformService.retrieveAllFunds();
LoanProductMapper rm = new LoanProductMapper(allowedCurrencies, allFunds);
String sql = "select " + rm.loanProductSchema() String sql = "select " + rm.loanProductSchema()
+ " where lp.org_id = ?"; + " where lp.org_id = ?";
@@ -132,17 +140,18 @@ public class LoanProductReadPlatformServiceImpl implements
return productData; return productData;
} }
private static final class LoanProductMapper implements private static final class LoanProductMapper implements RowMapper<LoanProductData> {
RowMapper<LoanProductData> {
private final List<CurrencyData> allowedCurrencies; private final List<CurrencyData> allowedCurrencies;
private final Collection<FundData> allFunds;
public LoanProductMapper(List<CurrencyData> allowedCurrencies) { public LoanProductMapper(List<CurrencyData> allowedCurrencies, Collection<FundData> allFunds) {
this.allowedCurrencies = allowedCurrencies; this.allowedCurrencies = allowedCurrencies;
this.allFunds = allFunds;
} }
public String loanProductSchema() { public String loanProductSchema() {
return "lp.id as id, 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, 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,"
@@ -160,6 +169,9 @@ public class LoanProductReadPlatformServiceImpl implements
String name = rs.getString("name"); String name = rs.getString("name");
String description = rs.getString("description"); String description = rs.getString("description");
Long fundId = rs.getLong("fundId");
FundData fund = findFundDataById(fundId, allFunds);
String currencyCode = rs.getString("currencyCode"); String currencyCode = rs.getString("currencyCode");
Integer currencyDigits = JdbcSupport.getInteger(rs, Integer currencyDigits = JdbcSupport.getInteger(rs,
"currencyDigits"); "currencyDigits");
@@ -216,11 +228,20 @@ public class LoanProductReadPlatformServiceImpl implements
numberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, repaymentEvery, interestRatePerPeriod,
annualInterestRate, repaymentFrequencyType, annualInterestRate, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestRateFrequencyType, amortizationType, interestType,
interestCalculationPeriodType); interestCalculationPeriodType, fund);
} }
private CurrencyData findCurrencyByCode(String currencyCode, private FundData findFundDataById(Long fundId, Collection<FundData> allFunds) {
List<CurrencyData> allowedCurrencies) { FundData match = null;
for (FundData fundData : allFunds) {
if (fundData.getId().equals(fundId)) {
match = fundData;
}
}
return match;
}
private CurrencyData findCurrencyByCode(String currencyCode, List<CurrencyData> allowedCurrencies) {
CurrencyData match = null; CurrencyData match = null;
for (CurrencyData currencyData : allowedCurrencies) { for (CurrencyData currencyData : allowedCurrencies) {
if (currencyData.getCode().equalsIgnoreCase(currencyCode)) { if (currencyData.getCode().equalsIgnoreCase(currencyCode)) {
@@ -251,8 +272,7 @@ public class LoanProductReadPlatformServiceImpl implements
} }
private void populateProductDataWithDropdownOptions( private void populateProductDataWithDropdownOptions(LoanProductData productData) {
LoanProductData productData) {
List<CurrencyData> currencyOptions = currencyReadPlatformService List<CurrencyData> currencyOptions = currencyReadPlatformService
.retrieveAllowedCurrencies(); .retrieveAllowedCurrencies();
@@ -267,6 +287,8 @@ public class LoanProductReadPlatformServiceImpl implements
List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions(); .retrieveInterestRateFrequencyTypeOptions();
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
productData.setCurrencyOptions(currencyOptions); productData.setCurrencyOptions(currencyOptions);
productData.setAmortizationTypeOptions(amortizationTypeOptions); productData.setAmortizationTypeOptions(amortizationTypeOptions);
productData.setInterestTypeOptions(interestTypeOptions); productData.setInterestTypeOptions(interestTypeOptions);
@@ -276,6 +298,7 @@ public class LoanProductReadPlatformServiceImpl implements
.setRepaymentFrequencyTypeOptions(repaymentFrequencyTypeOptions); .setRepaymentFrequencyTypeOptions(repaymentFrequencyTypeOptions);
productData productData
.setInterestRateFrequencyTypeOptions(interestRateFrequencyTypeOptions); .setInterestRateFrequencyTypeOptions(interestRateFrequencyTypeOptions);
productData.setFundOptions(fundOptions);
} }
} }
@@ -1,13 +1,16 @@
package org.mifosng.platform.loanproduct.service; package org.mifosng.platform.loanproduct.service;
import static org.mifosng.platform.Specifications.productThatMatches; import static org.mifosng.platform.Specifications.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.api.data.EntityIdentifier; 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.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.fund.domain.FundRepository;
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;
@@ -15,6 +18,7 @@ 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.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.organisation.domain.Organisation;
import org.mifosng.platform.security.PlatformSecurityContext; import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser; import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -27,12 +31,15 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final LoanProductRepository loanProductRepository; private final LoanProductRepository loanProductRepository;
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final FundRepository fundRepository;
@Autowired @Autowired
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanProductRepository loanProductRepository, final AprCalculator aprCalculator) { public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository) {
this.context = context; this.context = context;
this.loanProductRepository = loanProductRepository; this.loanProductRepository = loanProductRepository;
this.aprCalculator = aprCalculator; this.aprCalculator = aprCalculator;
this.fundRepository = fundRepository;
} }
@Transactional @Transactional
@@ -60,7 +67,10 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
BigDecimal annualInterestRate = this.aprCalculator.calculateFrom(interestFrequencyType, command.getInterestRatePerPeriodValue()); BigDecimal annualInterestRate = this.aprCalculator.calculateFrom(interestFrequencyType, command.getInterestRatePerPeriodValue());
LoanProduct loanproduct = new LoanProduct(currentUser.getOrganisation(), command.getName(), command.getDescription(), // associating fund with loan product at creation is optional for now.
Fund fund = findFundByIdIfProvided(currentUser.getOrganisation(), command.getFundId());
LoanProduct loanproduct = new LoanProduct(currentUser.getOrganisation(), fund, command.getName(), command.getDescription(),
currency, command.getPrincipalValue(), currency, command.getPrincipalValue(),
command.getInterestRatePerPeriodValue(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod, command.getInterestRatePerPeriodValue(), interestFrequencyType, annualInterestRate, interestMethod, interestCalculationPeriodMethod,
command.getRepaymentEveryValue(), repaymentFrequencyType, command.getNumberOfRepaymentsValue(), command.getRepaymentEveryValue(), repaymentFrequencyType, command.getNumberOfRepaymentsValue(),
@@ -71,6 +81,17 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
return new EntityIdentifier(loanproduct.getId()); return new EntityIdentifier(loanproduct.getId());
} }
private Fund findFundByIdIfProvided(final Organisation organisation, final Long fundId) {
Fund fund = null;
if (fundId != null) {
fund = this.fundRepository.findOne(fundsThatMatch(organisation, fundId));
if (fund == null) {
throw new FundNotFoundException(fundId);
}
}
return fund;
}
@Transactional @Transactional
@Override @Override
public EntityIdentifier updateLoanProduct(LoanProductCommand command) { public EntityIdentifier updateLoanProduct(LoanProductCommand command) {
@@ -85,7 +106,10 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
throw new LoanProductNotFoundException(command.getId()); throw new LoanProductNotFoundException(command.getId());
} }
product.update(command); // associating fund with loan product at creation is optional for now.
Fund fund = findFundByIdIfProvided(currentUser.getOrganisation(), command.getFundId());
product.update(command, fund);
this.loanProductRepository.save(product); this.loanProductRepository.save(product);