MIFOSX-232: WIP - split out terms and settings related fields on loan product along with charges and accounting related inputs.

This commit is contained in:
Keith Woodlock
2013-03-07 13:35:52 +00:00
parent 619ec9e8cc
commit 76d2dd6209
15 changed files with 773 additions and 619 deletions
@@ -17,6 +17,7 @@ import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePl
import org.mifosplatform.infrastructure.security.domain.PlatformUser;
import org.mifosplatform.infrastructure.security.service.PlatformPasswordEncoder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
/**
@@ -39,8 +40,8 @@ public final class JsonCommand {
private final Long apptableId;
private final Long datatableId;
private final Long codeId;
private String supportedEntityType;
private Long supportedEntityId;
private final String supportedEntityType;
private final Long supportedEntityId;
private final String transactionId;
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
@@ -86,6 +87,15 @@ public final class JsonCommand {
return this.parsedCommand;
}
public String jsonFragment(final String paramName) {
String jsonFragment = null;
if (this.parsedCommand.getAsJsonObject().has(paramName)) {
JsonElement fragment = this.parsedCommand.getAsJsonObject().get(paramName);
jsonFragment = this.fromApiJsonHelper.toJson(fragment);
}
return jsonFragment;
}
public Long commandId() {
return this.commandId;
}
@@ -347,6 +357,10 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractArrayNamed(parameterName, parsedCommand);
}
public JsonArray arrayOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, parsedCommand);
}
public boolean isChangeInPasswordParameterNamed(final String parameterName, final String existingValue,
final PlatformPasswordEncoder platformPasswordEncoder, final Long saltValue) {
boolean isChanged = false;
@@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterExcep
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@@ -46,7 +47,7 @@ public class FromJsonHelper {
return this.gsonConverter.fromJson(json, typeOfMap);
}
public <T> T fromJson(final String json, Class<T> classOfT) {
public <T> T fromJson(final String json, final Class<T> classOfT) {
return this.gsonConverter.fromJson(json, classOfT);
}
@@ -93,6 +94,10 @@ public class FromJsonHelper {
return helperDelegator.extractLongNamed(parameterName, element, parametersPassedInRequest);
}
public JsonArray extractJsonArrayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractJsonArrayNamed(parameterName, element);
}
public String[] extractArrayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractArrayNamed(parameterName, element, new HashSet<String>());
}
@@ -109,7 +114,7 @@ public class FromJsonHelper {
return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest);
}
public LocalDate extractLocalDateNamed(final String parameterName, JsonElement element) {
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
}
@@ -32,7 +32,7 @@ import com.google.gson.JsonPrimitive;
* Helper class to extract values of json named attributes.
*/
public class JsonParserHelper {
public boolean parameterExists(final String parameterName, final JsonElement element) {
return element.getAsJsonObject().has(parameterName);
}
@@ -137,19 +137,21 @@ public class JsonParserHelper {
}
return value;
}
/** Method used to extract integers from unformatted strings.
* Ex: "1" , "100002" etc
/**
* Method used to extract integers from unformatted strings. Ex: "1" ,
* "100002" etc
*
* Please note that this method does not support extracting Integers from locale
* specific formatted strings Ex "1,000" etc
* Please note that this method does not support extracting Integers from
* locale specific formatted strings Ex "1,000" etc
*
* @param parameterName
* @param element
* @param parametersPassedInRequest
* @return
*/
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element, final Set<String> parametersPassedInRequest) {
public Integer extractIntegerSansLocaleNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInRequest) {
Integer intValue = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
@@ -211,6 +213,19 @@ public class JsonParserHelper {
return arrayValue;
}
public JsonArray extractJsonArrayNamed(final String parameterName, final JsonElement element) {
JsonArray jsonArray = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has(parameterName)) {
jsonArray = object.get(parameterName).getAsJsonArray();
}
}
return jsonArray;
}
/**
* Used with the local date is in array format
*/
@@ -391,8 +406,8 @@ public class JsonParserHelper {
} catch (NumberFormatException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter "
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter " + parameterName
+ " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
@@ -400,7 +415,6 @@ public class JsonParserHelper {
}
}
public BigDecimal convertFrom(final String numericalValueFormatted, final String parameterName, final Locale clientApplicationLocale) {
if (clientApplicationLocale == null) {
@@ -11,11 +11,12 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
/**
* Immutable data object for charge data.
*/
public class ChargeData {
public class ChargeData implements Comparable<ChargeData> {
private final Long id;
private final String name;
@@ -72,36 +73,8 @@ public class ChargeData {
this.chargeTimeTypeOptions = chargeTimeTypeOptions;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public boolean isPenalty() {
return this.penalty;
}
public BigDecimal getAmount() {
return amount;
}
public CurrencyData getCurrency() {
return currency;
}
public EnumOptionData getChargeTimeType() {
return chargeTimeType;
}
public EnumOptionData getChargeCalculationType() {
return chargeCalculationType;
}
@Override
public boolean equals(Object obj) {
public boolean equals(final Object obj) {
ChargeData chargeData = (ChargeData) obj;
return this.id.equals(chargeData.id);
}
@@ -110,4 +83,16 @@ public class ChargeData {
public int hashCode() {
return this.id.hashCode();
}
@Override
public int compareTo(final ChargeData obj) {
if (obj == null) { return -1; }
return obj.id.compareTo(this.id);
}
public LoanChargeData toLoanChargeData() {
return LoanChargeData.newLoanChargeDetails(this.id, this.name, this.currency, this.amount, this.chargeTimeType,
this.chargeCalculationType, this.penalty);
}
}
@@ -14,8 +14,15 @@ import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -71,7 +78,7 @@ public class Charge extends AbstractPersistable<Long> {
}
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, boolean active) {
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active) {
this.name = name;
this.amount = amount;
this.currencyCode = currencyCode;
@@ -83,6 +90,24 @@ public class Charge extends AbstractPersistable<Long> {
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
LoanCharge rhs = (LoanCharge) obj;
return new EqualsBuilder().appendSuper(super.equals(obj)) //
.append(this.getId(), rhs.getId()) //
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(3, 5) //
.append(this.getId()) //
.toHashCode();
}
public String getName() {
return name;
}
@@ -180,8 +205,9 @@ public class Charge extends AbstractPersistable<Long> {
actualChanges.put(activeParamName, newValue);
this.active = newValue;
}
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
name); }
return actualChanges;
}
@@ -196,4 +222,15 @@ public class Charge extends AbstractPersistable<Long> {
this.deleted = true;
this.name = this.getId() + "_" + this.name;
}
public ChargeData toData() {
EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(this.chargeTime);
EnumOptionData chargeAppliesTo = ChargeEnumerations.chargeAppliesTo(this.chargeAppliesTo);
EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(this.chargeCalculation);
CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, null, null);
return ChargeData.instance(this.getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
penalty, active);
}
}
@@ -353,8 +353,7 @@ public class LoanAccountData {
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
for (ChargeData charge : product.charges()) {
charges.add(LoanChargeData.newLoanChargeDetails(charge.getId(), charge.getName(), charge.getCurrency(), charge.getAmount(),
charge.getChargeTimeType(), charge.getChargeCalculationType(), charge.isPenalty()));
charges.add(charge.toLoanChargeData());
}
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
@@ -57,12 +57,13 @@ import org.springframework.stereotype.Component;
public class LoanProductsApiResource {
private final Set<String> LOAN_PRODUCT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "description", "fundId",
"fundName", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "principal", "inArrearsTolerance",
"numberOfRepayments", "repaymentEvery", "interestRatePerPeriod", "annualInterestRate", "repaymentFrequencyType",
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn",
"lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions",
"chargeOptions", "accountingOptions", "accountingRule","accountingRuleOptions","accountingMappings","accountingMappingOptions"));
"fundName", "currency", "principal", "numberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod",
"interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName", "charges", "accountingRule",
"accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions",
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -129,7 +130,7 @@ public class LoanProductsApiResource {
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) {
public String retrieveTemplate(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
@@ -154,7 +155,8 @@ public class LoanProductsApiResource {
Map<String, Object> accountingMappings = null;
if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType().getId().intValue());
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct
.accountingRuleType().getId().intValue());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
}
@@ -182,7 +184,6 @@ public class LoanProductsApiResource {
final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeOptions.removeAll(productData.charges());
if (chargeOptions.isEmpty()) {
chargeOptions = null;
}
@@ -192,7 +193,6 @@ public class LoanProductsApiResource {
final List<EnumOptionData> interestTypeOptions = dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
final List<EnumOptionData> interestCalculationPeriodTypeOptions = dropdownReadPlatformService
.retrieveLoanInterestRateCalculatedInPeriodOptions();
final List<EnumOptionData> loanTermFrequencyTypeOptions = dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions();
@@ -204,29 +204,29 @@ public class LoanProductsApiResource {
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService
.retreiveTransactionProcessingStrategies();
Map<String, List<GLAccountData>> accountOptions= new HashMap<String, List<GLAccountData>>();
Map<String, List<GLAccountData>> accountOptions = new HashMap<String, List<GLAccountData>>();
List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
List<GLAccountData> incomeAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
List<GLAccountData> expenseAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
}
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions,accountingMappings);
interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions,
transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, accountingMappings);
}
}
@@ -33,47 +33,46 @@ public class LoanProductData {
private final String description;
private final Long fundId;
private final String fundName;
// terms
private final CurrencyData currency;
private final BigDecimal principal;
private final Integer numberOfRepayments;
private final Integer loanTermFrequency;
private final Integer repaymentEvery;
private final EnumOptionData loanTermFrequencyType;
private final EnumOptionData repaymentFrequencyType;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final BigDecimal interestRatePerPeriod;
private final EnumOptionData interestRateFrequencyType;
private final BigDecimal annualInterestRate;
// settings
private final EnumOptionData amortizationType;
private final EnumOptionData interestType;
private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final EnumOptionData accountingRule;
// charges
private final Collection<ChargeData> charges;
// accounting
private final EnumOptionData accountingRule;
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
// template related
private final Collection<CurrencyData> currencyOptions;
private final Collection<FundData> fundOptions;
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
private final List<EnumOptionData> amortizationTypeOptions;
private final List<EnumOptionData> interestTypeOptions;
private final List<EnumOptionData> interestCalculationPeriodTypeOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
private final List<EnumOptionData> loanTermFrequencyTypeOptions;
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
private final Collection<ChargeData> chargeOptions;
// accounting related template fields
private final List<EnumOptionData> accountingRuleOptions;
private final Map<String, List<GLAccountData>> accountingMappingOptions;
// accounting related mapping fields
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
/**
* Used when returning lookup information about loan product for dropdowns.
*/
@@ -83,11 +82,9 @@ public class LoanProductData {
final BigDecimal principal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = null;
final EnumOptionData repaymentFrequencyType = null;
final EnumOptionData interestRateFrequencyType = null;
final EnumOptionData amortizationType = null;
@@ -100,10 +97,10 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
@@ -114,11 +111,9 @@ public class LoanProductData {
final BigDecimal principal = null;
final BigDecimal tolerance = null;
final Integer numberOfRepayments = null;
final Integer loanTermFrequency = null;
final Integer repaymentEvery = null;
final BigDecimal interestRatePerPeriod = null;
final BigDecimal annualInterestRate = null;
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS);
@@ -132,25 +127,24 @@ public class LoanProductData {
final Collection<ChargeData> charges = null;
final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) {
return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions,
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions,
productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions,
productData.accountingMappingOptions, productData.accountingRuleOptions, accountingMappings);
productData.repaymentFrequencyTypeOptions, productData.interestRateFrequencyTypeOptions, productData.fundOptions,
productData.transactionProcessingStrategyOptions, productData.accountingMappingOptions, productData.accountingRuleOptions,
accountingMappings);
}
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer loanTermFrequency,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate,
final EnumOptionData loanTermFrequencyType, final EnumOptionData repaymentFrequencyType,
final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer repaymentEvery,
final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate, final EnumOptionData repaymentFrequencyType,
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
@@ -162,11 +156,11 @@ public class LoanProductData {
this.principal = principal;
this.inArrearsTolerance = tolerance;
this.numberOfRepayments = numberOfRepayments;
this.loanTermFrequency = loanTermFrequency;
// this.loanTermFrequency = loanTermFrequency;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
this.annualInterestRate = annualInterestRate;
this.loanTermFrequencyType = loanTermFrequencyType;
// this.loanTermFrequencyType = loanTermFrequencyType;
this.repaymentFrequencyType = repaymentFrequencyType;
this.interestRateFrequencyType = interestRateFrequencyType;
this.amortizationType = amortizationType;
@@ -186,7 +180,7 @@ public class LoanProductData {
this.amortizationTypeOptions = null;
this.interestTypeOptions = null;
this.interestCalculationPeriodTypeOptions = null;
this.loanTermFrequencyTypeOptions = null;
// this.loanTermFrequencyTypeOptions = null;
this.repaymentFrequencyTypeOptions = null;
this.interestRateFrequencyTypeOptions = null;
@@ -198,31 +192,29 @@ public class LoanProductData {
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions,
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
final List<EnumOptionData> loanTermFrequencyTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions,
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final Map<String, Object> accountingMappings) {
this.id = productData.id;
this.name = productData.name;
this.description = productData.description;
this.fundId = productData.fundId;
this.fundName = productData.fundName;
this.principal = productData.principal;
this.inArrearsTolerance = productData.inArrearsTolerance;
this.numberOfRepayments = productData.numberOfRepayments;
this.loanTermFrequency = productData.loanTermFrequency;
this.repaymentEvery = productData.repaymentEvery;
this.interestRatePerPeriod = productData.interestRatePerPeriod;
this.annualInterestRate = productData.annualInterestRate;
this.loanTermFrequencyType = productData.loanTermFrequencyType;
this.repaymentFrequencyType = productData.repaymentFrequencyType;
this.interestRateFrequencyType = productData.interestRateFrequencyType;
this.amortizationType = productData.amortizationType;
this.interestType = productData.interestType;
this.interestCalculationPeriodType = productData.interestCalculationPeriodType;
this.fundId = productData.fundId;
this.fundName = productData.fundName;
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
this.charges = nullIfEmpty(productData.charges());
this.accountingRule = productData.accountingRule;
@@ -234,17 +226,32 @@ public class LoanProductData {
this.currency = productData.currency;
}
this.fundOptions = fundOptions;
this.transactionProcessingStrategyOptions = transactionProcessingStrategyOptions;
this.transactionProcessingStrategyOptions = transactionStrategyOptions;
if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<TransactionProcessingStrategyData>(
this.transactionProcessingStrategyOptions);
this.transactionProcessingStrategyId = listOfOptions.get(0).id();
this.transactionProcessingStrategyName = listOfOptions.get(0).name();
} else {
this.transactionProcessingStrategyId = productData.transactionProcessingStrategyId;
this.transactionProcessingStrategyName = productData.transactionProcessingStrategyName;
}
this.amortizationTypeOptions = amortizationTypeOptions;
this.interestTypeOptions = interestTypeOptions;
this.interestCalculationPeriodTypeOptions = interestCalculationPeriodTypeOptions;
this.loanTermFrequencyTypeOptions = loanTermFrequencyTypeOptions;
this.repaymentFrequencyTypeOptions = repaymentFrequencyTypeOptions;
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
this.accountingMappingOptions = accountingMappingOptions;
this.accountingRuleOptions = accountingRuleOptions;
this.accountingMappings = accountingMappings;
if (accountingMappings == null || accountingMappings.isEmpty()) {
this.accountingMappings = null;
} else {
this.accountingMappings = accountingMappings;
}
}
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -315,10 +322,6 @@ public class LoanProductData {
return numberOfRepayments;
}
public Integer getLoanTermFrequency() {
return loanTermFrequency;
}
public Integer getRepaymentEvery() {
return repaymentEvery;
}
@@ -331,10 +334,6 @@ public class LoanProductData {
return annualInterestRate;
}
public EnumOptionData getLoanTermFrequencyType() {
return loanTermFrequencyType;
}
public EnumOptionData getRepaymentFrequencyType() {
return repaymentFrequencyType;
}
@@ -359,10 +358,6 @@ public class LoanProductData {
return fundOptions;
}
public Collection<TransactionProcessingStrategyData> getTransactionProcessingStrategyOptions() {
return transactionProcessingStrategyOptions;
}
public List<EnumOptionData> getAmortizationTypeOptions() {
return amortizationTypeOptions;
}
@@ -383,10 +378,6 @@ public class LoanProductData {
return interestRateFrequencyTypeOptions;
}
public List<EnumOptionData> getLoanTermFrequencyTypeOptions() {
return loanTermFrequencyTypeOptions;
}
public Collection<ChargeData> getChargeOptions() {
return chargeOptions;
}
@@ -5,13 +5,14 @@
*/
package org.mifosplatform.portfolio.loanproduct.data;
/**
* Immutable data object representing a transaction strategy option for a loan.
*/
public final class TransactionProcessingStrategyData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final String code;
@SuppressWarnings("unused")
private final String name;
public TransactionProcessingStrategyData(final Long id, final String code, final String name) {
@@ -19,4 +20,12 @@ public final class TransactionProcessingStrategyData {
this.code = code;
this.name = name;
}
public Long id() {
return this.id;
}
public String name() {
return this.name;
}
}
@@ -6,7 +6,7 @@
package org.mifosplatform.portfolio.loanproduct.domain;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -29,6 +29,8 @@ import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.springframework.data.jpa.domain.AbstractPersistable;
import com.google.gson.JsonArray;
/**
* Loan products allow for categorisation of an organisations loans into
* something meaningful to them.
@@ -39,7 +41,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
* They allow for constraints to be added at product level.
*/
@Entity
@Table(name = "m_product_loan" , uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name")})
@Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") })
public class LoanProduct extends AbstractPersistable<Long> {
@ManyToOne
@@ -50,7 +52,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false , unique = true)
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "description")
@@ -58,7 +60,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@ManyToMany
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
private Set<Charge> charges;
private List<Charge> charges;
@Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail;
@@ -67,7 +69,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
private Integer accountingRule;
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final Set<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
final String name = command.stringValueOfParameterNamed("name");
final String description = command.stringValueOfParameterNamed("description");
@@ -108,7 +110,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final Set<Charge> charges,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
final AccountingRuleType accountingRuleType) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
@@ -136,10 +138,6 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.loanProductRelatedDetail.getCurrency();
}
public Set<Charge> getCharges() {
return charges;
}
public void update(final Fund fund) {
this.fund = fund;
}
@@ -148,8 +146,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.transactionProcessingStrategy = strategy;
}
public void update(final Set<Charge> charges) {
this.charges = charges;
public boolean update(final List<Charge> newProductCharges) {
boolean updated = false;
if (this.charges != null) {
final Set<Charge> setOfCharges = new HashSet<Charge>(this.charges);
updated = setOfCharges.addAll(newProductCharges);
if (updated) {
this.charges = newProductCharges;
}
}
return updated;
}
public Integer getAccountingType() {
@@ -202,30 +209,17 @@ public class LoanProduct extends AbstractPersistable<Long> {
}
final String chargesParamName = "charges";
if (command.isChangeInArrayParameterNamed(chargesParamName, getChargesIds())) {
final String[] newValue = command.arrayValueOfParameterNamed(chargesParamName);
actualChanges.put(chargesParamName, newValue);
if (command.hasParameter(chargesParamName)) {
JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
if (jsonArray != null) {
}
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
}
return actualChanges;
}
private String[] getChargesIds() {
final List<String> chargeIds = new ArrayList<String>();
if (this.charges != null) {
for (Charge charge : this.charges) {
chargeIds.add(charge.getId().toString());
}
}
return chargeIds.toArray(new String[chargeIds.size()]);
}
public boolean isAccountingEnabled() {
return !AccountingRuleType.NONE.getValue().equals(this.accountingRule);
}
public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
}
@@ -35,10 +35,10 @@ public final class LoanProductCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType",
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingRule",
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "name", "description", "fundId",
"currencyCode", "digitsAfterDecimal", "principal", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType",
"interestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
"inArrearsTolerance", "transactionProcessingStrategyId", "charges", "accountingRule",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
@@ -74,6 +74,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
// terms
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
@@ -83,12 +84,6 @@ public final class LoanProductCommandFromApiJsonDeserializer {
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
@@ -99,14 +94,14 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
// settings
final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
@@ -118,6 +113,13 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1);
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -176,7 +178,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(String json) {
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
@@ -134,19 +134,16 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
final Integer accountingTypeId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingTypeId);
final Integer loanTermFrequency = repaymentEvery * numberOfRepayments;
final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingRuleId);
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId);
final EnumOptionData loanTermFrequencyType = LoanEnumerations.loanTermFrequencyType(repaymentFrequencyTypeId);
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
@@ -160,10 +157,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final EnumOptionData interestCalculationPeriodType = LoanEnumerations
.interestCalculationPeriodType(interestCalculationPeriodTypeId);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, repaymentEvery,
interestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
interestType, interestCalculationPeriodType, fundId, fundName, transactionStrategyId, transactionStrategyName,
this.charges, accountingRuleType);
}
}
@@ -5,9 +5,9 @@
*/
package org.mifosplatform.portfolio.loanproduct.service;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
@@ -16,9 +16,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
import org.mifosplatform.portfolio.charge.exception.ChargeIsNotActiveException;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.mifosplatform.portfolio.fund.domain.Fund;
import org.mifosplatform.portfolio.fund.domain.FundRepository;
import org.mifosplatform.portfolio.fund.exception.FundNotFoundException;
@@ -31,13 +29,15 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingS
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductCommandFromApiJsonDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@Service
public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanProductWritePlatformService {
@@ -49,7 +49,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private final AprCalculator aprCalculator;
private final FundRepository fundRepository;
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
private final ChargeRepository chargeRepository;
private final ChargeRepositoryWrapper chargeRepository;
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
@Autowired
@@ -57,7 +57,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanProductCommandFromApiJsonDeserializer fromApiJsonDeserializer, final LoanProductRepository loanProductRepository,
final AprCalculator aprCalculator, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final ChargeRepository chargeRepository, final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
final ChargeRepositoryWrapper chargeRepository,
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.loanProductRepository = loanProductRepository;
@@ -78,15 +79,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
this.fromApiJsonDeserializer.validateForCreate(command.json());
// associating fund with loan product at creation is optional for
// now.
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final Set<Charge> charges = this.assembleSetOfCharges(command, currencyCode);
final List<Charge> charges = this.assembleListOfProductCharges(command, currencyCode);
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
this.aprCalculator);
@@ -131,7 +130,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
try {
this.context.authenticatedUser();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
@@ -141,8 +139,6 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final Map<String, Object> changes = product.update(command, this.aprCalculator);
// associating fund with loan product at creation is optional for
// now.
if (changes.containsKey("fundId")) {
final Long fundId = (Long) changes.get("fundId");
final Fund fund = findFundByIdIfProvided(fundId);
@@ -156,10 +152,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
}
if (changes.containsKey("charges")) {
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
product.update(charges);
final List<Charge> productCharges = this.assembleListOfProductCharges(command, product.getCurrency().getCode());
boolean updated = product.update(productCharges);
if (!updated) {
changes.remove("charges");
}
}
// accounting related changes
boolean accountingTypeChanged = changes.containsKey("accountingRule");
final Map<String, Object> accountingMappingChanges = accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(
@@ -183,29 +182,33 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
}
private Set<Charge> assembleSetOfCharges(final JsonCommand command, final String currencyCode) {
private List<Charge> assembleListOfProductCharges(final JsonCommand command, final String currencyCode) {
final Set<Charge> charges = new HashSet<Charge>();
final String[] chargesArray = command.arrayValueOfParameterNamed("charges");
final List<Charge> charges = new ArrayList<Charge>();
String loanProductCurrencyCode = command.stringValueOfParameterNamed("currencyCode");
if (loanProductCurrencyCode == null) {
loanProductCurrencyCode = currencyCode;
}
if (!ObjectUtils.isEmpty(chargesArray)) {
for (final String chargeId : chargesArray) {
if (command.parameterExists("charges")) {
JsonArray chargesArray = command.arrayOfParameterNamed("charges");
if (chargesArray != null) {
for (int i = 0; i < chargesArray.size(); i++) {
final Long id = Long.valueOf(chargeId);
final Charge charge = this.chargeRepository.findOne(id);
if (charge == null || charge.isDeleted()) { throw new ChargeNotFoundException(id); }
if (!charge.isActive()) { throw new ChargeIsNotActiveException(id, charge.getName()); }
final JsonObject jsonObject = chargesArray.get(i).getAsJsonObject();
if (jsonObject.has("id")) {
final Long id = jsonObject.get("id").getAsLong();
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);
final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(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);
}
}
charges.add(charge);
}
}
@@ -224,6 +227,10 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final String name = command.stringValueOfParameterNamed("name");
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.name", "Loan product with name `" + name
+ "` already exists", "name", name);
} else if (realCause.getMessage().contains("Duplicate entry")) {
Object[] args = null;
throw new PlatformDataIntegrityException("error.msg.product.loan.duplicate.charge",
"Loan product may only have one charge of each type.`", "charges", args);
}
logAsErrorUnexpectedDataIntegrityException(dve);
@@ -234,5 +241,4 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
}
}