diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 9a9faf578..88ed51e5e 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -261,6 +261,21 @@ function getBaseURL(docURL)
Update a Staff Member |
|
+
+ | CHARGES | charges |
+ Create a Charge |
+ List Charges |
+ |
+ |
+
+
+ |
+ charges/{chargeId} |
+ |
+ Retrieve a Charge |
+ Update a Charge |
+ Delete a Charge |
+
@@ -2703,6 +2718,207 @@ Request Body:
+
+
+
+
+
Charges
+
Its typical for MFIs to add extra costs to their financial products. There are typically Fees or Penalites.
+
Charges is what we use to model both fees and penalities. For now charges can only be associated with loan products / loans.
+
+
+
+
+
+
+
Create/Define a Charge
+
Define a new charge that can later be associated with loan products or loans.
+
+
+ POST https://Domain Name/api/v1/charges
+ 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"
+}
+
+ {
+ "resourceId": 2
+}
+
+
+
+
+
+
+
+
Retrieve Charges
+
Returns the list of defined charges.
+
Example Requests:
+
charges
+
+
+ GET https://Domain Name/api/v1/charges
+ [
+ {
+ "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"
+ }
+ }
+]
+
+
+
+
+
+
+
+
Retrieve a Charge
+
Returns the details of a defined Charge.
+
Example Requests:
+
charges/2
+
+
+ GET https://Domain Name/api/v1/charges/2
+ {
+ "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"
+ }
+}
+
+
+
+
+
+
+
+
Update a Charge
+
Updates the details of a Charge.
+
+
+ PUT https://Domain Name/api/v1/charges/1
+ PUT charges/1
+Content-Type: application/json
+Request Body:
+{
+ "name": "Loan service fee(changed)"
+}
+
+ {
+ "resourceId": 2,
+ "changes": {
+ "name": "Loan service fee(changed)"
+ }
+}
+
+
+
+
+
+
+
+
Delete a Charge
+
Deletes a Charge.
+
+
+ DELETE https://Domain Name/api/v1/charges/2
+ DELETE charges/2
+Content-Type: application/json
+
+ {
+ "resourceId": 2
+}
+
+
+
+
@@ -3839,6 +4055,21 @@ Request body:
Update a Staff Member |
|
+
+ | CHARGES | charges |
+ Create a Charge |
+ List Charges |
+ |
+ |
+
+
+ |
+ charges/{chargeId} |
+ |
+ Retrieve a Charge |
+ Update a Charge |
+ Delete a Charge |
+
| DATA TABLE | datatables | | List Data Tables | | |
| datatables/register/{datatable}/{apptable} | Register Data Table | | | |
| datatables/deregister/{datatable} | Deregister Data Table | | | |
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/command/ChargeDefinitionCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/command/ChargeDefinitionCommand.java
deleted file mode 100644
index a61cda3a2..000000000
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/command/ChargeDefinitionCommand.java
+++ /dev/null
@@ -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
dataValidationErrors = new ArrayList();
-
- 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 dataValidationErrors = new ArrayList();
-
- 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); }
- }
-}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java
index eacba74ba..347284e76 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java
@@ -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 {
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 {
actualChanges.put(activeParamName, newValue);
this.active = newValue;
}
+
+ if (this.penalty && ChargeTimeType.fromInt(this.chargeTime).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
return actualChanges;
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java
index d44b209fc..6e1442cb8 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java
@@ -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);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeDueAtDisbursementCannotBePenaltyException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeDueAtDisbursementCannotBePenaltyException.java
new file mode 100644
index 000000000..2984bbfd6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeDueAtDisbursementCannotBePenaltyException.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeIsNotActiveException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeIsNotActiveException.java
index 7b3bce9eb..48690dd15 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeIsNotActiveException.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/ChargeIsNotActiveException.java
@@ -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);
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java
index 37efb3a85..8f1daf813 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java
@@ -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 {
+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