MIFOSX-42 Attach charges to loan products.

This commit is contained in:
Michal Dudzinski
2012-08-30 17:12:25 +02:00
parent 57b38e463a
commit f9d41aa500
15 changed files with 240 additions and 25 deletions
@@ -18,11 +18,13 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.api.data.ChargeData;
import org.mifosng.platform.api.data.EntityIdentifier; import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.LoanProductData; import org.mifosng.platform.api.data.LoanProductData;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService; import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService; import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService;
import org.mifosng.platform.api.infrastructure.ApiParameterHelper; import org.mifosng.platform.api.infrastructure.ApiParameterHelper;
import org.mifosng.platform.charge.service.ChargeReadPlatformService;
import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService; import org.mifosng.platform.loanproduct.service.LoanProductReadPlatformService;
import org.mifosng.platform.loanproduct.service.LoanProductWritePlatformService; import org.mifosng.platform.loanproduct.service.LoanProductWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +42,9 @@ public class LoanProductsApiResource {
@Autowired @Autowired
private LoanProductWritePlatformService loanProductWritePlatformService; private LoanProductWritePlatformService loanProductWritePlatformService;
@Autowired
private ChargeReadPlatformService chargeReadPlatformService;
@Autowired @Autowired
private ApiDataConversionService apiDataConversionService; private ApiDataConversionService apiDataConversionService;
@@ -67,7 +72,7 @@ public class LoanProductsApiResource {
Arrays.asList("id", "name", "description", "fundId", "fundName", "principal", "inArrearsTolerance", "numberOfRepayments", Arrays.asList("id", "name", "description", "fundId", "fundName", "principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"createdOn", "lastModifedOn") "charges", "createdOn", "lastModifedOn")
); );
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
@@ -97,9 +102,9 @@ public class LoanProductsApiResource {
Arrays.asList("id", "name", "description", "fundId", "fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName", Arrays.asList("id", "name", "description", "fundId", "fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance", "numberOfRepayments", "principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges",
"createdOn", "lastModifedOn","currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "createdOn", "lastModifedOn","currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions") "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions")
); );
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
@@ -124,7 +129,8 @@ public class LoanProductsApiResource {
"transactionProcessingStrategyId", "transactionProcessingStrategyName", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"principal", "inArrearsTolerance", "numberOfRepayments", "principal", "inArrearsTolerance", "numberOfRepayments",
"repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate",
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "charges",
"createdOn", "lastModifedOn") "createdOn", "lastModifedOn")
); );
@@ -138,7 +144,10 @@ 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", "transactionProcessingStrategyOptions")); "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions", "chargeOptions"));
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges();
chargeOptions.removeAll(loanProduct.getCharges());
loanProduct.setChargeOptions(chargeOptions);
} }
return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct); return this.apiJsonSerializerService.serializeLoanProductDataToJson(prettyPrint, responseParameters, loanProduct);
@@ -30,7 +30,9 @@ public class LoanProductCommand {
private final Integer amortizationType; private final Integer amortizationType;
private final Integer interestType; private final Integer interestType;
private final Integer interestCalculationPeriodType; private final Integer interestCalculationPeriodType;
private final String[] charges;
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,
@@ -39,7 +41,7 @@ public class LoanProductCommand {
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,
final Integer repaymentFrequencyType, final Integer interestRateFrequencyType, final Integer amortizationType, final Integer interestType, final Integer repaymentFrequencyType, final Integer interestRateFrequencyType, final Integer amortizationType, final Integer interestType,
final Integer interestCalculationPeriodType) { final Integer interestCalculationPeriodType, final String[] charges) {
this.modifiedParameters = modifiedParameters; this.modifiedParameters = modifiedParameters;
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -58,6 +60,7 @@ public class LoanProductCommand {
this.amortizationType = amortizationType; this.amortizationType = amortizationType;
this.interestType = interestType; this.interestType = interestType;
this.interestCalculationPeriodType = interestCalculationPeriodType; this.interestCalculationPeriodType = interestCalculationPeriodType;
this.charges = charges;
} }
public Long getId() { public Long getId() {
@@ -128,7 +131,11 @@ public class LoanProductCommand {
return interestCalculationPeriodType; return interestCalculationPeriodType;
} }
public boolean isNameChanged() { public String[] getCharges() {
return charges;
}
public boolean isNameChanged() {
return this.modifiedParameters.contains("name"); return this.modifiedParameters.contains("name");
} }
@@ -191,4 +198,8 @@ public class LoanProductCommand {
public boolean isInterestCalculationPeriodTypeChanged() { public boolean isInterestCalculationPeriodTypeChanged() {
return this.modifiedParameters.contains("interestCalculationPeriodType"); return this.modifiedParameters.contains("interestCalculationPeriodType");
} }
public boolean isChargesChanged() {
return this.modifiedParameters.contains("charges");
}
} }
@@ -127,4 +127,15 @@ public class ChargeData {
public List<EnumOptionData> getChargeTimeTypeOptions() { public List<EnumOptionData> getChargeTimeTypeOptions() {
return chargeTimeTypeOptions; return chargeTimeTypeOptions;
} }
@Override
public boolean equals(Object obj) {
ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.getId());
}
@Override
public int hashCode() {
return this.id.hashCode();
}
} }
@@ -36,6 +36,8 @@ public class LoanProductData {
private EnumOptionData interestType; private EnumOptionData interestType;
private EnumOptionData interestCalculationPeriodType; private EnumOptionData interestCalculationPeriodType;
private Collection<ChargeData> charges;
private DateTime createdOn; private DateTime createdOn;
private DateTime lastModifedOn; private DateTime lastModifedOn;
@@ -48,6 +50,7 @@ public class LoanProductData {
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>();
private List<EnumOptionData> loanTermFrequencyTypeOptions = new ArrayList<EnumOptionData>(); private List<EnumOptionData> loanTermFrequencyTypeOptions = new ArrayList<EnumOptionData>();
private Collection<ChargeData> chargeOptions = new ArrayList<ChargeData>();
public LoanProductData() { public LoanProductData() {
// //
@@ -354,4 +357,20 @@ public class LoanProductData {
List<EnumOptionData> interestCalculationPeriodTypeOptions) { List<EnumOptionData> interestCalculationPeriodTypeOptions) {
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions; this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
} }
public Collection<ChargeData> getCharges() {
return charges;
}
public void setCharges(Collection<ChargeData> charges) {
this.charges = charges;
}
public Collection<ChargeData> getChargeOptions() {
return chargeOptions;
}
public void setChargeOptions(Collection<ChargeData> chargeOptions) {
this.chargeOptions = chargeOptions;
}
} }
@@ -345,13 +345,14 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
throw new InvalidJsonException(); throw new InvalidJsonException();
} }
Type typeOfMap = new TypeToken<Map<String, String>>(){}.getType(); Type typeOfMap = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap); Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
Set<String> supportedParams = new HashSet<String>( Set<String> supportedParams = new HashSet<String>(
Arrays.asList("name", "description", "fundId", "transactionProcessingStrategyId", "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",
"charges", "locale")
); );
checkForUnsupportedParameters(requestMap, supportedParams); checkForUnsupportedParameters(requestMap, supportedParams);
@@ -376,10 +377,28 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
Integer amortizationTypeValue = extractIntegerParameter("amortizationType", requestMap, modifiedParameters); Integer amortizationTypeValue = extractIntegerParameter("amortizationType", requestMap, modifiedParameters);
Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters); Integer interestTypeValue = extractIntegerParameter("interestType", requestMap, modifiedParameters);
Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters); Integer interestCalculationPeriodTypeValue = extractIntegerParameter("interestCalculationPeriodType", requestMap, modifiedParameters);
// check array
JsonParser parser = new JsonParser();
String[] charges = null;
JsonElement element = parser.parse(json);
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has("charges")) {
modifiedParameters.add("charges");
JsonArray array = object.get("charges").getAsJsonArray();
charges = new String[array.size()];
for (int i=0; i<array.size(); i++) {
charges[i] = array.get(i).getAsString();
}
}
}
//
return new LoanProductCommand(modifiedParameters, resourceIdentifier, name, description, fundId, transactionProcessingStrategyId, 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, charges);
} }
@Override @Override
@@ -72,12 +72,13 @@ public class GoogleGsonApiJsonSerializerService implements
"annualInterestRate", "repaymentFrequencyType", "annualInterestRate", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestRateFrequencyType", "amortizationType",
"interestType", "interestCalculationPeriodType", "interestType", "interestCalculationPeriodType",
"charges",
"createdOn", "lastModifedOn", "currencyOptions", "createdOn", "lastModifedOn", "currencyOptions",
"amortizationTypeOptions", "interestTypeOptions", "amortizationTypeOptions", "interestTypeOptions",
"interestCalculationPeriodTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "fundOptions", "interestRateFrequencyTypeOptions", "fundOptions",
"transactionProcessingStrategyOptions")); "transactionProcessingStrategyOptions", "chargeOptions"));
private static final Set<String> SAVINGS_PRODUCT_DATA_PARAMETERS = new HashSet<String>( private static final Set<String> SAVINGS_PRODUCT_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "name", "description", "createdOn", Arrays.asList("id", "name", "description", "createdOn",
@@ -71,6 +71,14 @@ public class Charge extends AbstractAuditableCustom<AppUser, Long> {
return amount; return amount;
} }
public String getCurrencyCode() {
return currencyCode;
}
public boolean isActive() {
return active;
}
public boolean isDeleted() { public boolean isDeleted() {
return deleted; return deleted;
} }
@@ -11,4 +11,8 @@ public interface ChargeReadPlatformService {
ChargeData retrieveCharge(Long chargeId); ChargeData retrieveCharge(Long chargeId);
ChargeData retrieveNewChargeDetails(); ChargeData retrieveNewChargeDetails();
Collection<ChargeData> retrieveLoanApplicableCharges();
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId);
} }
@@ -93,6 +93,29 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
allowedChargeAppliesToOptions, allowedChargeTimeOptions); allowedChargeAppliesToOptions, allowedChargeTimeOptions);
} }
@Override
public Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId) {
this.context.authenticatedUser();
ChargeMapper rm = new ChargeMapper();
String sql = "select " + rm.loanProductChargeSchema() + " where c.is_deleted=0 and plc.product_loan_id=?";
return this.jdbcTemplate.query(sql, rm, new Object[] {loanProductId});
}
@Override
public Collection<ChargeData> retrieveLoanApplicableCharges() {
this.context.authenticatedUser();
ChargeMapper rm = new ChargeMapper();
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 ";
return this.jdbcTemplate.query(sql, rm, new Object[] {ChargeAppliesTo.LOAN.getValue()});
}
private static final class ChargeMapper implements RowMapper<ChargeData> { private static final class ChargeMapper implements RowMapper<ChargeData> {
public String chargeSchema(){ public String chargeSchema(){
@@ -104,6 +127,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
"join m_organisation_currency oc on c.currency_code = oc.code"; "join m_organisation_currency oc on c.currency_code = oc.code";
} }
public String loanProductChargeSchema(){
return chargeSchema() + " join m_product_loan_charge plc on plc.charge_id = c.id";
}
@Override @Override
public ChargeData mapRow(ResultSet rs, int rowNum) throws SQLException { public ChargeData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id"); Long id = rs.getLong("id");
@@ -0,0 +1,12 @@
package org.mifosng.platform.exceptions;
/**
* {@link AbstractPlatformDomainRuleException} thrown when charge is not active
*/
public class ChargeIsNotActiveException extends AbstractPlatformDomainRuleException{
public ChargeIsNotActiveException(final Long id) {
super("error.msg.charge.is.not.active", "Charge with identifier " + id + " is not active", id);
}
}
@@ -0,0 +1,12 @@
package org.mifosng.platform.exceptions;
/**
* {@link AbstractPlatformDomainRuleException} thrown when currency mismatch occurs
*/
public class InvalidCurrencyException extends AbstractPlatformDomainRuleException {
public InvalidCurrencyException(final String entity, final String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
super("error.msg."+entity+"."+postFix+".invalid.currency", defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -1,16 +1,20 @@
package org.mifosng.platform.loan.domain; package org.mifosng.platform.loan.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Set;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embedded; import javax.persistence.Embedded;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.LoanProductCommand; import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.charge.domain.Charge;
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.fund.domain.Fund;
@@ -43,6 +47,12 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "description") @Column(name = "description")
private String description; private String description;
@ManyToMany
@JoinTable(name = "m_product_loan_charge",
joinColumns = @JoinColumn(name = "product_loan_id"),
inverseJoinColumns = @JoinColumn(name = "charge_id"))
private Set<Charge> charges;
@Embedded @Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail; private final LoanProductRelatedDetail loanProductRelatedDetail;
@@ -58,7 +68,7 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
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, final Set<Charge> charges) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim(); this.name = name.trim();
@@ -67,7 +77,11 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
} else { } else {
this.description = null; this.description = null;
} }
if (charges != null){
this.charges = charges;
}
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency,
defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod, interestMethod, interestCalculationPeriodMethod,
@@ -138,7 +152,8 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
return transactionProcessingStrategy; return transactionProcessingStrategy;
} }
public void update(final LoanProductCommand command, final Fund fund, final LoanTransactionProcessingStrategy strategy) { public void update(final LoanProductCommand command, final Fund fund, final LoanTransactionProcessingStrategy strategy,
final Set<Charge> charges) {
if (command.isNameChanged()) { if (command.isNameChanged()) {
this.name = command.getName(); this.name = command.getName();
@@ -155,7 +170,11 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
if (command.isTransactionProcessingStrategyChanged()) { if (command.isTransactionProcessingStrategyChanged()) {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategy = strategy;
} }
if (command.isChargesChanged()){
this.charges = charges;
}
this.loanProductRelatedDetail.update(command); this.loanProductRelatedDetail.update(command);
} }
} }
@@ -45,7 +45,7 @@ public class LoanProductCommandValidator {
baseDataValidator.reset().anyOfNotNull(command.getFundId(), command.getTransactionProcessingStrategyId(), command.getName(), command.getDescription(), command.getCurrencyCode(), command.getDigitsAfterDecimal(), baseDataValidator.reset().anyOfNotNull(command.getFundId(), command.getTransactionProcessingStrategyId(), command.getName(), command.getDescription(), 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(), command.getCharges());
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);
@@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.mifosng.platform.api.data.ChargeData;
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.FundData;
@@ -14,6 +15,7 @@ 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.api.data.TransactionProcessingStrategyData;
import org.mifosng.platform.charge.service.ChargeReadPlatformService;
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;
@@ -38,6 +40,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final LoanDropdownReadPlatformService dropdownReadPlatformService; private final LoanDropdownReadPlatformService dropdownReadPlatformService;
private final FundReadPlatformService fundReadPlatformService; private final FundReadPlatformService fundReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService;
@Autowired @Autowired
public LoanProductReadPlatformServiceImpl( public LoanProductReadPlatformServiceImpl(
@@ -45,11 +48,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final CurrencyReadPlatformService currencyReadPlatformService, final CurrencyReadPlatformService currencyReadPlatformService,
final FundReadPlatformService fundReadPlatformService, final FundReadPlatformService fundReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final LoanDropdownReadPlatformService dropdownReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService,
final TenantAwareRoutingDataSource dataSource) { final TenantAwareRoutingDataSource dataSource) {
this.context = context; this.context = context;
this.currencyReadPlatformService = currencyReadPlatformService; this.currencyReadPlatformService = currencyReadPlatformService;
this.fundReadPlatformService = fundReadPlatformService; this.fundReadPlatformService = fundReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
@@ -64,6 +69,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
LoanProductData productData = this.jdbcTemplate.queryForObject(sql, LoanProductData productData = this.jdbcTemplate.queryForObject(sql,
rm, new Object[] { loanProductId }); rm, new Object[] { loanProductId });
Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId);
productData.setCharges(charges);
populateProductDataWithDropdownOptions(productData); populateProductDataWithDropdownOptions(productData);
return productData; return productData;
@@ -81,7 +89,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
String sql = "select " + rm.loanProductSchema(); String sql = "select " + rm.loanProductSchema();
return this.jdbcTemplate.query(sql, rm, new Object[] {}); Collection<LoanProductData> loanProducts = this.jdbcTemplate.query(sql, rm, new Object[] {});
for (LoanProductData loanProduct : loanProducts){
Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProduct.getId());
loanProduct.setCharges(charges);
}
return loanProducts;
} }
@Override @Override
@@ -112,6 +126,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
productData.setInterestCalculationPeriodType(LoanEnumerations.interestCalculationPeriodType(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD)); productData.setInterestCalculationPeriodType(LoanEnumerations.interestCalculationPeriodType(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD));
productData.setChargeOptions(this.chargeReadPlatformService.retrieveLoanApplicableCharges());
populateProductDataWithDropdownOptions(productData); populateProductDataWithDropdownOptions(productData);
productData.setPrincipal(BigDecimal.ZERO); productData.setPrincipal(BigDecimal.ZERO);
@@ -1,11 +1,18 @@
package org.mifosng.platform.loanproduct.service; package org.mifosng.platform.loanproduct.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
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.charge.domain.Charge;
import org.mifosng.platform.charge.domain.ChargeRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.exceptions.ChargeIsNotActiveException;
import org.mifosng.platform.exceptions.ChargeNotFoundException;
import org.mifosng.platform.exceptions.FundNotFoundException; import org.mifosng.platform.exceptions.FundNotFoundException;
import org.mifosng.platform.exceptions.InvalidCurrencyException;
import org.mifosng.platform.exceptions.LoanProductNotFoundException; import org.mifosng.platform.exceptions.LoanProductNotFoundException;
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException; import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosng.platform.fund.domain.Fund; import org.mifosng.platform.fund.domain.Fund;
@@ -23,6 +30,7 @@ import org.mifosng.platform.security.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@Service @Service
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService { public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
@@ -32,15 +40,18 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final AprCalculator aprCalculator; private final AprCalculator aprCalculator;
private final FundRepository fundRepository; private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository; private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final ChargeRepository chargeRepository;
@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 LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) { final AprCalculator aprCalculator, final FundRepository fundRepository, final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepository chargeRepository) {
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; this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
this.chargeRepository = chargeRepository;
} }
@Transactional @Transactional
@@ -71,13 +82,15 @@ 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()); LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
final Set<Charge> charges = this.assembleSetOfCharges(command, null);
LoanProduct loanproduct = new LoanProduct(fund, loanTransactionProcessingStrategy, LoanProduct loanproduct = new LoanProduct(fund, loanTransactionProcessingStrategy,
command.getName(), command.getDescription(), 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(),
amortizationMethod, command.getInArrearsTolerance()); amortizationMethod, command.getInArrearsTolerance(), charges);
this.loanProductRepository.save(loanproduct); this.loanProductRepository.save(loanproduct);
@@ -130,11 +143,45 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
if (command.isTransactionProcessingStrategyChanged()) { if (command.isTransactionProcessingStrategyChanged()) {
findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId()); findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
} }
product.update(command, fund, loanTransactionProcessingStrategy); final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
product.update(command, fund, loanTransactionProcessingStrategy, charges);
this.loanProductRepository.save(product); this.loanProductRepository.save(product);
return new EntityIdentifier(Long.valueOf(product.getId())); return new EntityIdentifier(Long.valueOf(product.getId()));
} }
private Set<Charge> assembleSetOfCharges(final LoanProductCommand command, final String currencyCode) {
Set<Charge> charges = new HashSet<Charge>();
String[] chargesArray = command.getCharges();
String loanProductCurrencyCode = command.getCurrencyCode();
if (loanProductCurrencyCode == null){
loanProductCurrencyCode = currencyCode;
}
if (command.isChargesChanged() && !ObjectUtils.isEmpty(chargesArray)) {
for (String chargeId : chargesArray) {
Long id = Long.valueOf(chargeId);
Charge charge = this.chargeRepository.findOne(id);
if (charge == null || charge.isDeleted()) {
throw new ChargeNotFoundException(id);
}
if (!charge.isActive()){
throw new ChargeIsNotActiveException(id);
}
if (!loanProductCurrencyCode.equals(charge.getCurrencyCode())){
String errorMessage = "Charge and Loan Product must have the same currency.";
throw new InvalidCurrencyException("charge", "attach.to.loan.product", errorMessage);
}
charges.add(charge);
}
}
return charges;
}
} }