update api docs for charges, update validation of charges.

This commit is contained in:
Keith Woodlock
2013-01-28 22:29:26 +00:00
parent a99da667fb
commit af7c9b7989
14 changed files with 513 additions and 242 deletions
+231
View File
@@ -261,6 +261,21 @@ function getBaseURL(docURL)
<td><a href="#staff_update">Update a Staff Member</a></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#charges">CHARGES</a></td><td>charges</td>
<td><a href="#charges_create">Create a Charge</a></td>
<td><a href="#charges_list">List Charges</a></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>charges/{chargeId}</td>
<td></td>
<td><a href="#charges_retrieve">Retrieve a Charge</a></td>
<td><a href="#charges_update">Update a Charge</a></td>
<td><a href="#charges_delete">Delete a Charge</a></td>
</tr>
</table>
</div>
</div>
@@ -2703,6 +2718,207 @@ Request Body:
</div>
</div>
<a id="charges" name="charges" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h3>Charges</h3>
<p>Its typical for MFIs to add extra costs to their financial products. There are typically <b>Fees</b> or <b>Penalites</b>.</p>
<p>Charges is what we use to model both fees and penalities. For now charges can only be associated with loan products / loans.</p>
</div>
</div>
<a id="charges_create" name="staff_create" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Create/Define a Charge</h4>
<p>Define a new charge that can later be associated with loan products or loans.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/charges</code>
<code class="method-request">POST charges
Content-Type: application/json
Request Body:
{
"name": "Loan service fee",
"chargeAppliesTo": 1,
"currencyCode": "USD",
"locale": "en",
"amount": "230.56",
"chargeTimeType": "1",
"chargeCalculationType": "1"
}
</code>
<code class="method-response">{
"resourceId": 2
}
</code>
</div>
</div>
<a id="charges_list" name="charges_list" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve Charges</h4>
<p>Returns the list of defined charges.</p>
<p>Example Requests: </p>
<div class=apiClick>charges</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://Domain Name/api/v1/charges</code>
<code class="method-response">[
{
"id": 2,
"name": "Loan service fee",
"active": false,
"penalty": false,
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"amount": 230.56,
"chargeTimeType": {
"id": 1,
"code": "chargeTimeType.disbursement",
"value": "Disbursement"
},
"chargeAppliesTo": {
"id": 1,
"code": "chargeAppliesTo.loan",
"value": "Loan"
},
"chargeCalculationType": {
"id": 1,
"code": "chargeCalculationType.flat",
"value": "Flat"
}
},
{
"id": 1,
"name": "newcharge",
"active": true,
"penalty": false,
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"amount": 456,
"chargeTimeType": {
"id": 1,
"code": "chargeTimeType.disbursement",
"value": "Disbursement"
},
"chargeAppliesTo": {
"id": 1,
"code": "chargeAppliesTo.loan",
"value": "Loan"
},
"chargeCalculationType": {
"id": 1,
"code": "chargeCalculationType.flat",
"value": "Flat"
}
}
]
</code>
</div>
</div>
<a id="charges_retrieve" name="charges_retrieve" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Retrieve a Charge</h4>
<p>Returns the details of a defined Charge.</p>
<p>Example Requests: </p>
<div class=apiClick>charges/2</div>
</div>
<div class="method-example">
<code class="method-declaration">GET https://Domain Name/api/v1/charges/2</code>
<code class="method-response">{
"id": 2,
"name": "Loan service fee",
"active": false,
"penalty": false,
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"amount": 230.56,
"chargeTimeType": {
"id": 1,
"code": "chargeTimeType.disbursement",
"value": "Disbursement"
},
"chargeAppliesTo": {
"id": 1,
"code": "chargeAppliesTo.loan",
"value": "Loan"
},
"chargeCalculationType": {
"id": 1,
"code": "chargeCalculationType.flat",
"value": "Flat"
}
}
</code>
</div>
</div>
<a id="charges_update" name="charges_update" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Update a Charge</h4>
<p>Updates the details of a Charge.</p>
</div>
<div class="method-example">
<code class="method-declaration">PUT https://Domain Name/api/v1/charges/1</code>
<code class="method-request">PUT charges/1
Content-Type: application/json
Request Body:
{
"name": "Loan service fee(changed)"
}
</code>
<code class="method-response">{
"resourceId": 2,
"changes": {
"name": "Loan service fee(changed)"
}
}
</code>
</div>
</div>
<a id="charges_delete" name="charges_delete" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Delete a Charge</h4>
<p>Deletes a Charge.</p>
</div>
<div class="method-example">
<code class="method-declaration">DELETE https://Domain Name/api/v1/charges/2</code>
<code class="method-request">DELETE charges/2
Content-Type: application/json
</code>
<code class="method-response">{
"resourceId": 2
}
</code>
</div>
</div>
<!-- start of datatables api docs -->
<a id="datatables" name="datatables" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
@@ -3839,6 +4055,21 @@ Request body:
<td><a href="#staff_update">Update a Staff Member</a></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#charges">CHARGES</a></td><td>charges</td>
<td><a href="#charges_create">Create a Charge</a></td>
<td><a href="#charges_list">List Charges</a></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>charges/{chargeId}</td>
<td></td>
<td><a href="#charges_retrieve">Retrieve a Charge</a></td>
<td><a href="#charges_update">Update a Charge</a></td>
<td><a href="#charges_delete">Delete a Charge</a></td>
</tr>
<tr><td><a href="#datatables">DATA TABLE</a></td><td>datatables</td><td></td><td><a href="#datatables_list">List Data Tables</a></td><td></td><td></td></tr>
<tr class="alt"><td></td><td>datatables/register/{datatable}/{apptable}</td><td><a href="#datatables_register">Register Data Table</a></td><td></td><td></td><td></td></tr>
<tr><td></td><td>datatables/deregister/{datatable}</td><td><a href="#datatables_deregister">Deregister Data Table</a></td><td></td><td></td><td></td></tr>
@@ -1,111 +0,0 @@
package org.mifosplatform.portfolio.charge.command;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
/**
* Immutable data object for creating and modifying defined charges.
*/
public class ChargeDefinitionCommand {
private final String name;
private final BigDecimal amount;
private final String currencyCode;
private final Integer chargeTimeType;
private final Integer chargeAppliesTo;
private final Integer chargeCalculationType;
private final Boolean penalty;
private final Boolean active;
public ChargeDefinitionCommand(final String name, final BigDecimal amount, final String currencyCode, final Integer chargeTimeType,
final Integer chargeAppliesTo, final Integer chargeCalculationType, final Boolean penalty, final Boolean active) {
this.name = name;
this.amount = amount;
this.currencyCode = currencyCode;
this.chargeTimeType = chargeTimeType;
this.chargeAppliesTo = chargeAppliesTo;
this.chargeCalculationType = chargeCalculationType;
this.penalty = penalty;
this.active = active;
}
public String getName() {
return name;
}
public BigDecimal getAmount() {
return amount;
}
public String getCurrencyCode() {
return currencyCode;
}
public Integer getChargeCalculationType() {
return chargeCalculationType;
}
public Integer getChargeAppliesTo() {
return chargeAppliesTo;
}
public Integer getChargeTimeType() {
return chargeTimeType;
}
public boolean isPenalty() {
return penalty;
}
public boolean isFee() {
return !penalty;
}
public boolean isActive() {
return active;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
baseDataValidator.reset().parameter("name").value(this.name).notBlank().notExceedingLengthOf(100);
baseDataValidator.reset().parameter("amount").value(this.amount).notNull().positiveAmount();
baseDataValidator.reset().parameter("currencyCode").value(this.currencyCode).notBlank();
baseDataValidator.reset().parameter("chargeAppliesTo").value(this.chargeAppliesTo).notNull().inMinMaxRange(1, 1);
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType).notNull().inMinMaxRange(1, 2);
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculationType).notNull().inMinMaxRange(1, 4);
if (this.penalty && this.chargeTimeType.equals(Integer.valueOf(1))) {
// FIXME - KW - cannot have penalty charge at time of disbursement
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
baseDataValidator.reset().parameter("name").value(this.name).ignoreIfNull().notBlank().notExceedingLengthOf(100);
baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter("currencyCode").value(this.currencyCode).ignoreIfNull().notBlank();
// FIXME - kw - need to check if parameter exists in update and if so that its not null
baseDataValidator.reset().parameter("chargeAppliesTo").value(this.chargeAppliesTo).ignoreIfNull().notNull().inMinMaxRange(1, 1);
baseDataValidator.reset().parameter("chargeTimeType").value(this.chargeTimeType).ignoreIfNull().notNull().inMinMaxRange(1, 2);
baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculationType).ignoreIfNull().notNull()
.inMinMaxRange(1, 4);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -10,6 +10,7 @@ import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.charge.exception.ChargeDueAtDisbursementCannotBePenaltyException;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -74,6 +75,7 @@ public class Charge extends AbstractPersistable<Long> {
this.chargeCalculation = chargeCalculationType.getValue();
this.penalty = penalty;
this.active = active;
if (penalty && chargeTime.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
}
public String getName() {
@@ -173,6 +175,8 @@ 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); }
return actualChanges;
}
@@ -1,9 +1,9 @@
package org.mifosplatform.portfolio.charge.domain;
public enum ChargeTimeType {
INVALID(0, "chargeTimeType.invalid"),
DISBURSEMENT(1, "chargeTimeType.disbursement"),
INVALID(0, "chargeTimeType.invalid"), //
DISBURSEMENT(1, "chargeTimeType.disbursement"), //
SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate");
private final Integer value;
@@ -24,17 +24,21 @@ public enum ChargeTimeType {
public static ChargeTimeType fromInt(final Integer chargeTime) {
ChargeTimeType chargeTimeType = ChargeTimeType.INVALID;
switch(chargeTime){
switch (chargeTime) {
case 1:
chargeTimeType = DISBURSEMENT;
break;
break;
case 2:
chargeTimeType = SPECIFIED_DUE_DATE;
break;
break;
default:
chargeTimeType = INVALID;
break;
break;
}
return chargeTimeType;
}
public boolean isTimeOfDisbursement() {
return ChargeTimeType.DISBURSEMENT.getValue().equals(this.value);
}
}
@@ -0,0 +1,14 @@
package org.mifosplatform.portfolio.charge.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when attempting to crate
* charge of type penalty due at disbursement .
*/
public class ChargeDueAtDisbursementCannotBePenaltyException extends AbstractPlatformDomainRuleException {
public ChargeDueAtDisbursementCannotBePenaltyException(final String name) {
super("error.msg.charge.due.at.disbursement.cannot.be.penalty", "Charge '" + name + "' is invalid.", name, name);
}
}
@@ -7,7 +7,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul
*/
public class ChargeIsNotActiveException extends AbstractPlatformDomainRuleException {
public ChargeIsNotActiveException(final Long id, final String name) {
super("error.msg.charge.is.not.active", "Charge '" + name + "' with identifier " + id + " is not active", name, id);
}
public ChargeIsNotActiveException(final Long id, final String name) {
super("error.msg.charge.is.not.active", "Charge '" + name + "' with identifier " + id + " is not active", name, id);
}
}
@@ -2,16 +2,20 @@ package org.mifosplatform.portfolio.charge.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.charge.command.ChargeDefinitionCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,7 +23,7 @@ import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public final class ChargeDefinitionCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<ChargeDefinitionCommand> {
public final class ChargeDefinitionCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
@@ -35,27 +39,104 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer extends Abstra
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Override
public ChargeDefinitionCommand commandFromApiJson(final String json) {
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
final JsonElement element = fromApiJsonHelper.parse(json);
final String name = fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element.getAsJsonObject());
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeTimeType", element);
final Integer chargeAppliesTo = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeAppliesTo", element);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("chargeCalculationType", element);
final Integer chargeAppliesTo = fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(1, 1);
final Boolean penalty = fromApiJsonHelper.extractBooleanNamed("penalty", element);
final Boolean active = fromApiJsonHelper.extractBooleanNamed("active", element);
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(1, 2);
return new ChargeDefinitionCommand(name, amount, currencyCode, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty,
active);
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
if (fromApiJsonHelper.parameterExists("penalty", element)) {
final Boolean penalty = fromApiJsonHelper.extractBooleanNamed("penalty", element);
baseDataValidator.reset().parameter("penalty").value(penalty).notNull();
}
if (fromApiJsonHelper.parameterExists("active", element)) {
final Boolean active = fromApiJsonHelper.extractBooleanNamed("active", element);
baseDataValidator.reset().parameter("active").value(active).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("charge");
final JsonElement element = fromApiJsonHelper.parse(json);
if (fromApiJsonHelper.parameterExists("name", element)) {
final String name = fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
}
if (fromApiJsonHelper.parameterExists("currencyCode", element)) {
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
}
if (fromApiJsonHelper.parameterExists("amount", element)) {
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("amount", element.getAsJsonObject());
baseDataValidator.reset().parameter("amount").value(amount).notNull().positiveAmount();
}
if (fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) {
final Integer chargeAppliesTo = fromApiJsonHelper.extractIntegerNamed("chargeAppliesTo", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull().inMinMaxRange(1, 1);
}
if (fromApiJsonHelper.parameterExists("chargeTimeType", element)) {
final Integer chargeTimeType = fromApiJsonHelper.extractIntegerNamed("chargeTimeType", element, Locale.getDefault());
baseDataValidator.reset().parameter("chargeTimeType").value(chargeTimeType).notNull().inMinMaxRange(1, 2);
}
if (fromApiJsonHelper.parameterExists("chargeCalculationType", element)) {
final Integer chargeCalculationType = fromApiJsonHelper.extractIntegerNamed("chargeCalculationType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType).notNull().inMinMaxRange(1, 4);
}
if (fromApiJsonHelper.parameterExists("penalty", element)) {
final Boolean penalty = fromApiJsonHelper.extractBooleanNamed("penalty", element);
baseDataValidator.reset().parameter("penalty").value(penalty).notNull();
}
if (fromApiJsonHelper.parameterExists("active", element)) {
final Boolean active = fromApiJsonHelper.extractBooleanNamed("active", element);
baseDataValidator.reset().parameter("active").value(active).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -3,9 +3,7 @@ package org.mifosplatform.portfolio.charge.service;
import java.util.Collection;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
// FIXME - KW - break out methods that belong to product charges and loan charges
public interface ChargeReadPlatformService {
Collection<ChargeData> retrieveAllCharges();
@@ -17,10 +15,4 @@ public interface ChargeReadPlatformService {
Collection<ChargeData> retrieveLoanApplicableCharges(boolean feeChargesOnly);
Collection<ChargeData> retrieveLoanProductCharges(Long loanProductId);
Collection<LoanChargeData> retrieveLoanCharges(Long loanId);
LoanChargeData retrieveLoanChargeDetails(Long loanChargeId, Long loanId);
ChargeData retrieveLoanChargeTemplate();
}
@@ -6,7 +6,6 @@ import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
@@ -16,7 +15,6 @@ import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformServi
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.domain.ChargeAppliesTo;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -93,31 +91,6 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId });
}
@Override
public Collection<LoanChargeData> retrieveLoanCharges(final Long loanId) {
this.context.authenticatedUser();
final LoanChargeMapper rm = new LoanChargeMapper();
// TODO - KW - check to see if its important that 'c.is_deleted=0' is
// required here?
final String sql = "select " + rm.loanChargeSchema() + " where c.is_deleted=0 and lc.loan_id=? "
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
}
@Override
public LoanChargeData retrieveLoanChargeDetails(final Long id, final Long loanId) {
this.context.authenticatedUser();
final LoanChargeMapper rm = new LoanChargeMapper();
final String sql = "select " + rm.loanChargeSchema() + " where lc.id=? and lc.loan_id=?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { id, loanId });
}
@Override
public Collection<ChargeData> retrieveLoanApplicableCharges(final boolean feeChargesOnly) {
this.context.authenticatedUser();
@@ -134,16 +107,6 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
return this.jdbcTemplate.query(sql, rm, new Object[] { ChargeAppliesTo.LOAN.getValue() });
}
@Override
public ChargeData retrieveLoanChargeTemplate() {
this.context.authenticatedUser();
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions);
}
private static final class ChargeMapper implements RowMapper<ChargeData> {
public String chargeSchema() {
@@ -185,60 +148,8 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
final boolean penalty = rs.getBoolean("penalty");
final boolean active = rs.getBoolean("active");
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, penalty, active);
}
}
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
public String loanChargeSchema() {
return "lc.id as id, c.id as chargeId, c.name as name, " + "lc.amount as amountDue, "
+ "lc.amount_paid_derived as amountPaid, " + "lc.amount_waived_derived as amountWaived, "
+ "lc.amount_writtenoff_derived as amountWrittenOff, " + "lc.amount_outstanding_derived as amountOutstanding, "
+ "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, "
+ "lc.charge_time_enum as chargeTime, " + "lc.is_penalty as penalty, "
+ "lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.charge_calculation_enum as chargeCalculation, "
+ "c.currency_code as currencyCode, oc.name as currencyName, "
+ "oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, "
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
+ "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id ";
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final String name = rs.getString("name");
final BigDecimal amount = rs.getBigDecimal("amountDue");
final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
final BigDecimal amountWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWaived");
final BigDecimal amountWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, currencyDisplaySymbol,
currencyNameCode);
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty);
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, penalty,
active);
}
}
}
@@ -7,7 +7,6 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
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.command.ChargeDefinitionCommand;
import org.mifosplatform.portfolio.charge.domain.Charge;
import org.mifosplatform.portfolio.charge.domain.ChargeRepository;
import org.mifosplatform.portfolio.charge.exception.ChargeNotFoundException;
@@ -42,8 +41,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
try {
this.context.authenticatedUser();
final ChargeDefinitionCommand chargeDefinitionCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
chargeDefinitionCommand.validateForCreate();
this.fromApiJsonDeserializer.validateForCreate(command.json());
final Charge charge = Charge.fromJson(command);
this.chargeRepository.save(charge);
@@ -62,8 +60,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
try {
this.context.authenticatedUser();
final ChargeDefinitionCommand chargeDefinitionCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
chargeDefinitionCommand.validateForUpdate();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final Charge chargeForUpdate = this.chargeRepository.findOne(chargeId);
if (chargeForUpdate == null) { throw new ChargeNotFoundException(chargeId); }
@@ -31,6 +31,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -48,17 +49,20 @@ public class LoanChargesApiResource {
private final PlatformSecurityContext context;
private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final DefaultToApiJsonSerializer<LoanChargeData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public LoanChargesApiResource(final PlatformSecurityContext context, final ChargeReadPlatformService chargeReadPlatformService,
final LoanChargeReadPlatformService loanChargeReadPlatformService,
final DefaultToApiJsonSerializer<LoanChargeData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context;
this.chargeReadPlatformService = chargeReadPlatformService;
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
@@ -93,7 +97,7 @@ public class LoanChargesApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
final LoanChargeData loanCharge = this.chargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId);
final LoanChargeData loanCharge = this.loanChargeReadPlatformService.retrieveLoanChargeDetails(loanChargeId, loanId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanCharge, RESPONSE_DATA_PARAMETERS);
@@ -52,6 +52,7 @@ import org.mifosplatform.portfolio.loanaccount.gaurantor.data.GuarantorData;
import org.mifosplatform.portfolio.loanaccount.gaurantor.service.GuarantorReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@@ -88,6 +89,7 @@ public class LoansApiResource {
private final LoanDropdownReadPlatformService dropdownReadPlatformService;
private final FundReadPlatformService fundReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final LoanScheduleCalculationPlatformService calculationPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final GuarantorReadPlatformService guarantorReadPlatformService;
@@ -102,7 +104,7 @@ public class LoansApiResource {
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
final LoanProductReadPlatformService loanProductReadPlatformService,
final LoanDropdownReadPlatformService dropdownReadPlatformService, final FundReadPlatformService fundReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService,
final LoanScheduleCalculationPlatformService calculationPlatformService,
final StaffReadPlatformService staffReadPlatformService, final GuarantorReadPlatformService guarantorReadPlatformService,
final DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer,
@@ -116,6 +118,7 @@ public class LoansApiResource {
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.fundReadPlatformService = fundReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
this.loanChargeReadPlatformService = loanChargeReadPlatformService;
this.calculationPlatformService = calculationPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.guarantorReadPlatformService = guarantorReadPlatformService;
@@ -154,7 +157,7 @@ public class LoansApiResource {
final boolean feeChargesOnly = false;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
ChargeData chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
ChargeData chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
LoanBasicDetailsData loanBasicDetails;
Long officeId;
@@ -258,7 +261,7 @@ public class LoansApiResource {
}
if (associationParameters.contains("charges")) {
charges = this.chargeReadPlatformService.retrieveLoanCharges(loanId);
charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) {
charges = null; // set back to null so doesnt appear in JSON
// is no charges exist.
@@ -293,7 +296,7 @@ public class LoansApiResource {
repaymentStrategyOptions = this.dropdownReadPlatformService.retreiveTransactionProcessingStrategies();
final boolean feeChargesOnly = false;
chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
chargeTemplate = this.chargeReadPlatformService.retrieveLoanChargeTemplate();
chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate();
}
final LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, convenienceDataRequired, repaymentSchedule,
@@ -0,0 +1,15 @@
package org.mifosplatform.portfolio.loanaccount.service;
import java.util.Collection;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
public interface LoanChargeReadPlatformService {
ChargeData retrieveLoanChargeTemplate();
Collection<LoanChargeData> retrieveLoanCharges(Long loanId);
LoanChargeData retrieveLoanChargeDetails(Long loanChargeId, Long loanId);
}
@@ -0,0 +1,126 @@
package org.mifosplatform.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.charge.service.ChargeDropdownReadPlatformService;
import org.mifosplatform.portfolio.charge.service.ChargeEnumerations;
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
@Autowired
public LoanChargeReadPlatformServiceImpl(final PlatformSecurityContext context,
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final TenantAwareRoutingDataSource dataSource) {
this.context = context;
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
public String schema() {
return "lc.id as id, c.id as chargeId, c.name as name, " + "lc.amount as amountDue, "
+ "lc.amount_paid_derived as amountPaid, " + "lc.amount_waived_derived as amountWaived, "
+ "lc.amount_writtenoff_derived as amountWrittenOff, " + "lc.amount_outstanding_derived as amountOutstanding, "
+ "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, "
+ "lc.charge_time_enum as chargeTime, " + "lc.is_penalty as penalty, "
+ "lc.due_for_collection_as_of_date as dueAsOfDate, " + "lc.charge_calculation_enum as chargeCalculation, "
+ "c.currency_code as currencyCode, oc.name as currencyName, "
+ "oc.decimal_places as currencyDecimalPlaces, oc.display_symbol as currencyDisplaySymbol, "
+ "oc.internationalized_name_code as currencyNameCode from m_charge c "
+ "join m_organisation_currency oc on c.currency_code = oc.code " + "join m_loan_charge lc on lc.charge_id = c.id ";
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final String name = rs.getString("name");
final BigDecimal amount = rs.getBigDecimal("amountDue");
final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
final BigDecimal amountWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWaived");
final BigDecimal amountWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, currencyDisplaySymbol,
currencyNameCode);
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty);
}
}
@Override
public ChargeData retrieveLoanChargeTemplate() {
this.context.authenticatedUser();
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions);
}
@Override
public LoanChargeData retrieveLoanChargeDetails(final Long id, final Long loanId) {
this.context.authenticatedUser();
final LoanChargeMapper rm = new LoanChargeMapper();
final String sql = "select " + rm.schema() + " where lc.id=? and lc.loan_id=?";
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { id, loanId });
}
@Override
public Collection<LoanChargeData> retrieveLoanCharges(final Long loanId) {
this.context.authenticatedUser();
final LoanChargeMapper rm = new LoanChargeMapper();
// TODO - KW - check to see if its important that 'c.is_deleted=0' is
// required here?
final String sql = "select " + rm.schema() + " where c.is_deleted=0 and lc.loan_id=? "
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
}
}