minor changes to client images and updates to documentation
This commit is contained in:
@@ -203,6 +203,26 @@
|
|||||||
<td><a href="#client_identifiers_update">Update a Client Identifier</a></td>
|
<td><a href="#client_identifiers_update">Update a Client Identifier</a></td>
|
||||||
<td><a href="#client_identifiers_delete">Delete a Client Identifier</a></td>
|
<td><a href="#client_identifiers_delete">Delete a Client Identifier</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="alt">
|
||||||
|
<td><a href="#client_images">Client Images</a></td>
|
||||||
|
<td>clients/{clientId}/images</td>
|
||||||
|
<td><a href="#client_images_create">Upload an Image for a Client (as DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_retrieve">Get Client Image (DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_update">Update Client Image (DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_delete">Delete Client Image</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#client_images_create_form">
|
||||||
|
Upload an Image for a Client (Multi-part form data)</a>
|
||||||
|
</td>
|
||||||
|
<td><a href="#client_images_retrieve_binary">Get Client Image (Binary file)</a></td>
|
||||||
|
<td><a href="#client_images_update_form">
|
||||||
|
Update Client Image (Multi-part form data)</a>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2297,6 +2317,225 @@ No Request Body:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- start of Client Images API docs-->
|
||||||
|
|
||||||
|
<a id="client_images" name="client_images" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h3>Client Images</h3>
|
||||||
|
<p>The current API provides support for the addition of a single
|
||||||
|
image for a Client<br/>
|
||||||
|
Allowed formats: JPEG (.jpg or .jpeg), GIF (.gif) and PNG (.png)
|
||||||
|
</p>
|
||||||
|
<p> The API supports two different Approaches for manipulating Client Images
|
||||||
|
<ul>
|
||||||
|
<li><a href="http://en.wikipedia.org/wiki/Data_URI_scheme" target="_blank"> Data URI's: </a>
|
||||||
|
For easier manipulation by Javascript clients etc in supported Browsers.
|
||||||
|
</li>
|
||||||
|
<li><a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2">
|
||||||
|
Multi-part form data:</a>
|
||||||
|
Images can be uploaded using Multi part forms and downloaded as regular binary files
|
||||||
|
</li>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_retrieve" name="client_images_retrieve"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Get Client Image (DATA URI)</h4>
|
||||||
|
<p>Example Requests:</p>
|
||||||
|
<div class=apiClick>clients/1/images</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
|
||||||
|
bWFnZVJlYWR5ccllPAAAAJ1JREFUeNpi+P//PwMIA4E9EG8E4idQDGLbw+WhiiqA+D8OXAFVAzbp
|
||||||
|
DxBvB2JLIGaGYkuoGEjOhhFIHAbij0BdPgxYACMj42ogJQpifwBiXSDeC8JIbt4LxSC5DyxQjTeB
|
||||||
|
+BeaYb+Q5EBOAVutCzMJHUNNPADzzDokiYdAfAmJvwLkGeTgWQfyKZICS6hYBTwc0QL8ORSjBDhA
|
||||||
|
gAEAOg13B6R/SAgAAAAASUVORK5CYII=
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_retrieve_binary" name="client_images_retrieve_binary"
|
||||||
|
class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Get Client Image (Binary file)</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
GET https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST clients/1/images
|
||||||
|
Accept: application/octet-stream
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
<i>Not shown</i>: The corresponding binary (image) file
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_create" name="client_images_create" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Upload an Image for a Client (Data URI)</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST clients/1/images
|
||||||
|
Content-Type: text/plain
|
||||||
|
Request Body:
|
||||||
|
data:image/png;base64,iVBORw0KGgoAA
|
||||||
|
AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l
|
||||||
|
EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6
|
||||||
|
P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 1,
|
||||||
|
"changes": {},
|
||||||
|
"resourceIdentifier": "1"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_create_form" name="client_images_create_form" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Upload an Image for a Client (Multi-part Form data)</h4>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>file
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
The form should contain a required named body part with the
|
||||||
|
name "file". <br/> <br/>
|
||||||
|
If you are using a HTML form, a snippet like
|
||||||
|
<small> <input type="file" name="file"></input> </small>
|
||||||
|
can be used for uploading the image file
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
POST https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
POST clients/1/images
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
Request Body: <i>Not shown</i>
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 1,
|
||||||
|
"changes": {},
|
||||||
|
"resourceIdentifier": "1"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_update" name="client_images_update" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Update a Client Image (Data URI)</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
PUT https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
PUT clients/1/images
|
||||||
|
Content-Type: text/plain
|
||||||
|
Request Body:
|
||||||
|
data:image/png;base64,iVBORw0KGgoAA
|
||||||
|
AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l
|
||||||
|
EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6
|
||||||
|
P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 1,
|
||||||
|
"changes": {},
|
||||||
|
"resourceIdentifier": "1"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_update_form" name="client_images_update_form" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Update a Client Image (Multi-part Form data)</h4>
|
||||||
|
<table class=matrixHeading>
|
||||||
|
<tr class="matrixHeadingBG">
|
||||||
|
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr class=alt>
|
||||||
|
<td>file
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
The form should contain a required named body part with the
|
||||||
|
name "file". <br/> <br/>
|
||||||
|
If you are using a HTML form, a snippet like
|
||||||
|
<small> <input type="file" name="file"></input> </small>
|
||||||
|
can be used for uploading the image file
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
PUT https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
PUT clients/1/images
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
Request Body: <i>Not shown</i>
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="client_images_delete" name="client_images_delete" class="old-syle-anchor"> </a>
|
||||||
|
<div class="method-section">
|
||||||
|
<div class="method-description">
|
||||||
|
<h4>Delete a Client Image</h4>
|
||||||
|
</div>
|
||||||
|
<div class="method-example">
|
||||||
|
<code class="method-declaration">
|
||||||
|
DELETE https://DomainName/api/v1/clients/{clientId}/images
|
||||||
|
</code>
|
||||||
|
<code class="method-request">
|
||||||
|
DELETE clients/1/images
|
||||||
|
Content-Type: application/json
|
||||||
|
No Request Body:
|
||||||
|
</code>
|
||||||
|
<code class="method-response">
|
||||||
|
{
|
||||||
|
"resourceId": 1,
|
||||||
|
"changes": {},
|
||||||
|
"resourceIdentifier": "1"
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- start of Groups api docs -->
|
<!-- start of Groups api docs -->
|
||||||
<a id="groups" name="groups" class="old-syle-anchor"> </a>
|
<a id="groups" name="groups" class="old-syle-anchor"> </a>
|
||||||
@@ -11619,6 +11858,26 @@ Content-Type: application/json
|
|||||||
<td><a href="#client_identifiers_update">Update a Client Identifier</a></td>
|
<td><a href="#client_identifiers_update">Update a Client Identifier</a></td>
|
||||||
<td><a href="#client_identifiers_delete">Delete a Client Identifier</a></td>
|
<td><a href="#client_identifiers_delete">Delete a Client Identifier</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#client_images">Client Images</a></td>
|
||||||
|
<td>clients/{clientId}/images</td>
|
||||||
|
<td><a href="#client_images_create">Upload an Image for a Client (as DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_retrieve">Get Client Image (DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_update">Update Client Image (DATA URI)</a></td>
|
||||||
|
<td><a href="#client_images_delete">Delete Client Image</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="alt">
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td><a href="#client_images_create_form">
|
||||||
|
Upload an Image for a Client (Multi-part form data)</a>
|
||||||
|
</td>
|
||||||
|
<td><a href="#client_images_retrieve_binary">Get Client Image (Binary file)</a></td>
|
||||||
|
<td><a href="#client_images_update_form">
|
||||||
|
Update Client Image (Multi-part form data)</a>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
<!-- loans api full matrix-->
|
<!-- loans api full matrix-->
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#loans">Loans</a></td>
|
<td><a href="#loans">Loans</a></td>
|
||||||
|
|||||||
+55
-14
@@ -14,6 +14,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException;
|
||||||
@@ -31,6 +32,49 @@ public class FileUtils {
|
|||||||
|
|
||||||
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx";
|
||||||
|
|
||||||
|
public static enum IMAGE_MIME_TYPE {
|
||||||
|
GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private IMAGE_MIME_TYPE(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static enum IMAGE_FILE_EXTENSION {
|
||||||
|
GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private IMAGE_FILE_EXTENSION(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static enum IMAGE_DATA_URI_SUFFIX {
|
||||||
|
GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG(
|
||||||
|
"data:" + IMAGE_MIME_TYPE.PNG.getValue() + ";base64,");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private IMAGE_DATA_URI_SUFFIX(final String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static Random random = new Random();
|
public static Random random = new Random();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,7 +182,8 @@ public class FileUtils {
|
|||||||
* @param mimeType
|
* @param mimeType
|
||||||
*/
|
*/
|
||||||
public static void validateImageMimeType(String mimeType) {
|
public static void validateImageMimeType(String mimeType) {
|
||||||
if (!(mimeType.equalsIgnoreCase("image/gif") || mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) { throw new ImageUploadException(); }
|
if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType
|
||||||
|
.equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { throw new ImageUploadException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,19 +194,15 @@ public class FileUtils {
|
|||||||
public static Base64EncodedImage extractImageFromDataURL(String dataURL) {
|
public static Base64EncodedImage extractImageFromDataURL(String dataURL) {
|
||||||
String fileExtension = "";
|
String fileExtension = "";
|
||||||
String base64EncodedString = null;
|
String base64EncodedString = null;
|
||||||
String gifDataURLSnippet = "data:image/gif;base64,";
|
if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.GIF.getValue())) {
|
||||||
String pngDataURLSnippet = "data:image/png;base64,";
|
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.GIF.getValue(), "");
|
||||||
String jpegDataURLSnippet = "data:image/jpeg;base64,";
|
fileExtension = IMAGE_FILE_EXTENSION.GIF.getValue();
|
||||||
|
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.PNG.getValue())) {
|
||||||
if (dataURL.contains(gifDataURLSnippet)) {
|
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.PNG.getValue(), "");
|
||||||
base64EncodedString = dataURL.replaceAll(gifDataURLSnippet, "");
|
fileExtension = IMAGE_FILE_EXTENSION.PNG.getValue();
|
||||||
fileExtension = ".gif";
|
} else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.JPEG.getValue())) {
|
||||||
} else if (dataURL.contains(pngDataURLSnippet)) {
|
base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.JPEG.getValue(), "");
|
||||||
base64EncodedString = dataURL.replaceAll(pngDataURLSnippet, "");
|
fileExtension = IMAGE_FILE_EXTENSION.JPEG.getValue();
|
||||||
fileExtension = ".png";
|
|
||||||
} else if (dataURL.contains(jpegDataURLSnippet)) {
|
|
||||||
base64EncodedString = dataURL.replaceAll(jpegDataURLSnippet, "");
|
|
||||||
fileExtension = ".jpeg";
|
|
||||||
} else {
|
} else {
|
||||||
throw new ImageDataURLNotValidException();
|
throw new ImageDataURLNotValidException();
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -11,14 +11,14 @@ public class LoanChargeCannotBeDeletedException extends AbstractPlatformDomainRu
|
|||||||
|
|
||||||
/*** enum of reasons of why Loan Charge cannot be waived **/
|
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||||
public static enum LOAN_CHARGE_CANNOT_BE_DELETED_REASON {
|
public static enum LOAN_CHARGE_CANNOT_BE_DELETED_REASON {
|
||||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_APPROVED;
|
ALREADY_PAID, ALREADY_WAIVED, LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE;
|
||||||
|
|
||||||
public String errorMessage() {
|
public String errorMessage() {
|
||||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||||
return "This loan charge has been partially/completely paid";
|
return "This loan charge has been partially/completely paid";
|
||||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
return "This loan charge has already been waived";
|
return "This loan charge has already been waived";
|
||||||
} else if (name().toString().equalsIgnoreCase("LOAN_APPROVED")) { return "This charge cannot be deleted as the loan it is associated with has already been approved"; }
|
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "This charge cannot be deleted as the loan it is associated with is not in submitted and pending approval stage"; }
|
||||||
return name().toString();
|
return name().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ public class LoanChargeCannotBeDeletedException extends AbstractPlatformDomainRu
|
|||||||
return "error.msg.loan.charge.already.paid";
|
return "error.msg.loan.charge.already.paid";
|
||||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
return "error.msg.loan.charge.already.waived";
|
return "error.msg.loan.charge.already.waived";
|
||||||
} else if (name().toString().equalsIgnoreCase("LOAN_APPROVED")) { return "error.msg.loan.charge.associated.loan.already.approved"; }
|
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "error.msg.loan.charge.associated.loan.not.in.submitted.and.pending.approval.stage"; }
|
||||||
return name().toString();
|
return name().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
package org.mifosplatform.portfolio.charge.exception;
|
||||||
|
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class LoanChargeCannotBeUpdatedException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||||
|
public static enum LOAN_CHARGE_CANNOT_BE_UPDATED_REASON {
|
||||||
|
ALREADY_PAID, ALREADY_WAIVED, LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE;
|
||||||
|
|
||||||
|
public String errorMessage() {
|
||||||
|
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||||
|
return "This loan charge has been partially/completely paid";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
|
return "This loan charge has already been waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "This charge cannot be updated as the loan it is associated with is not in submitted and pending approval stage"; }
|
||||||
|
return name().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String errorCode() {
|
||||||
|
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||||
|
return "error.msg.loan.charge.already.paid";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
|
return "error.msg.loan.charge.already.waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "error.msg.loan.charge.associated.loan.not.in.submitted.and.pending.approval.stage"; }
|
||||||
|
return name().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanChargeCannotBeUpdatedException(final LOAN_CHARGE_CANNOT_BE_UPDATED_REASON reason, final Long loanChargeId) {
|
||||||
|
super(reason.errorCode(), reason.errorMessage(), loanChargeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+130
-105
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.client.api;
|
package org.mifosplatform.portfolio.client.api;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
@@ -17,12 +18,18 @@ import javax.ws.rs.Path;
|
|||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
import org.mifosplatform.infrastructure.core.api.ApiConstants;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage;
|
||||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
import org.mifosplatform.infrastructure.core.service.FileUtils;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_DATA_URI_SUFFIX;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_FILE_EXTENSION;
|
||||||
|
import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_MIME_TYPE;
|
||||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||||
import org.mifosplatform.portfolio.client.exception.ImageNotFoundException;
|
import org.mifosplatform.portfolio.client.exception.ImageNotFoundException;
|
||||||
@@ -42,128 +49,146 @@ import com.sun.jersey.multipart.FormDataParam;
|
|||||||
@Scope("singleton")
|
@Scope("singleton")
|
||||||
public class ClientImagesApiResource {
|
public class ClientImagesApiResource {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final ClientReadPlatformService clientReadPlatformService;
|
private final ClientReadPlatformService clientReadPlatformService;
|
||||||
private final ClientWritePlatformService clientWritePlatformService;
|
private final ClientWritePlatformService clientWritePlatformService;
|
||||||
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
private final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ClientImagesApiResource(final PlatformSecurityContext context,
|
public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService,
|
||||||
final ClientReadPlatformService readPlatformService,
|
final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
||||||
final ClientWritePlatformService clientWritePlatformService,
|
this.context = context;
|
||||||
final DefaultToApiJsonSerializer<ClientData> toApiJsonSerializer) {
|
this.clientReadPlatformService = readPlatformService;
|
||||||
this.context = context;
|
this.clientWritePlatformService = clientWritePlatformService;
|
||||||
this.clientReadPlatformService = readPlatformService;
|
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||||
this.clientWritePlatformService = clientWritePlatformService;
|
}
|
||||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload images through multi-part form upload
|
* Upload images through multi-part form upload
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String addNewClientImage(
|
public String addNewClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") final Long fileSize,
|
||||||
@PathParam("clientId") final Long clientId,
|
@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails,
|
||||||
@HeaderParam("Content-Length") final Long fileSize,
|
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
||||||
@FormDataParam("file") final InputStream inputStream,
|
|
||||||
@FormDataParam("file") final FormDataContentDisposition fileDetails,
|
|
||||||
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
|
||||||
|
|
||||||
// TODO: vishwas might need more advances validation (like reading magic
|
// TODO: vishwas might need more advances validation (like reading magic
|
||||||
// number) for handling malicious clients
|
// number) for handling malicious clients
|
||||||
// and clients not setting mime type
|
// and clients not setting mime type
|
||||||
FileUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
FileUtils.validateClientImageNotEmpty(fileDetails.getFileName());
|
||||||
FileUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
FileUtils.validateImageMimeType(bodyPart.getMediaType().toString());
|
||||||
FileUtils.validateFileSizeWithinPermissibleRange(fileSize,
|
FileUtils.validateFileSizeWithinPermissibleRange(fileSize, fileDetails.getFileName(), ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
||||||
fileDetails.getFileName(),
|
|
||||||
ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB);
|
|
||||||
|
|
||||||
final CommandProcessingResult result = this.clientWritePlatformService
|
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
||||||
.saveOrUpdateClientImage(clientId, fileDetails.getFileName(),
|
inputStream);
|
||||||
inputStream);
|
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload image as a Data URL (essentially a base64 encoded stream)
|
* Upload image as a Data URL (essentially a base64 encoded stream)
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
public String addNewClientImage(@PathParam("clientId") final Long clientId,
|
public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
|
||||||
final String jsonRequestBody) {
|
|
||||||
|
|
||||||
final Base64EncodedImage base64EncodedImage = FileUtils
|
final Base64EncodedImage base64EncodedImage = FileUtils.extractImageFromDataURL(jsonRequestBody);
|
||||||
.extractImageFromDataURL(jsonRequestBody);
|
|
||||||
|
|
||||||
final CommandProcessingResult result = this.clientWritePlatformService
|
final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
||||||
.saveOrUpdateClientImage(clientId, base64EncodedImage);
|
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a base 64 encoded client image
|
* Returns a base 64 encoded client image Data URI
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.TEXT_PLAIN })
|
||||||
public String retrieveClientImage(@PathParam("clientId") final Long clientId) {
|
public String retrieveClientImage(@PathParam("clientId") final Long clientId) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||||
|
|
||||||
final ClientData clientData = this.clientReadPlatformService
|
final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId);
|
||||||
.retrieveIndividualClient(clientId);
|
|
||||||
|
|
||||||
if (clientData.imageKeyDoesNotExist()) {
|
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
||||||
throw new ImageNotFoundException("clients", clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Base64.encodeFromFile(clientData.imageKey());
|
// TODO: Need a better way of determining image type
|
||||||
}
|
String imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.JPEG.getValue();
|
||||||
|
if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||||
|
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.GIF.getValue();
|
||||||
|
} else if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||||
|
imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.PNG.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeFromFile(clientData.imageKey());
|
||||||
* This method is added only for consistency with other URL patterns and for
|
|
||||||
* maintaining consistency of usage of the HTTP "verb" at the client side
|
|
||||||
*/
|
|
||||||
@PUT
|
|
||||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
|
||||||
public String updateClientImage(
|
|
||||||
@PathParam("clientId") final Long clientId,
|
|
||||||
@HeaderParam("Content-Length") final Long fileSize,
|
|
||||||
@FormDataParam("file") final InputStream inputStream,
|
|
||||||
@FormDataParam("file") final FormDataContentDisposition fileDetails,
|
|
||||||
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
|
||||||
|
|
||||||
return addNewClientImage(clientId, fileSize, inputStream, fileDetails,
|
return clientImageAsBase64Text;
|
||||||
bodyPart);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
@GET
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
|
||||||
public String deleteClientImage(@PathParam("clientId") final Long clientId) {
|
public Response downloadClientImage(@PathParam("clientId") final Long clientId) {
|
||||||
this.clientWritePlatformService.deleteClientImage(clientId);
|
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(
|
context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE");
|
||||||
clientId));
|
final ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId);
|
||||||
}
|
|
||||||
|
if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); }
|
||||||
|
|
||||||
|
File image = new File(clientData.imageKey());
|
||||||
|
String imageName = image.getName();
|
||||||
|
ResponseBuilder response = Response.ok(image);
|
||||||
|
response.header("Content-Disposition", "attachment; filename=\"" + imageName + "\"");
|
||||||
|
|
||||||
|
// TODO: Need a better way of determining image type
|
||||||
|
|
||||||
|
// determine image type
|
||||||
|
String contentType = IMAGE_MIME_TYPE.JPEG.getValue();
|
||||||
|
if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.GIF.getValue())) {
|
||||||
|
contentType = IMAGE_MIME_TYPE.GIF.getValue();
|
||||||
|
} else if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.PNG.getValue())) {
|
||||||
|
contentType = IMAGE_MIME_TYPE.PNG.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
response.header("Content-Type", contentType);
|
||||||
|
return response.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is added only for consistency with other URL patterns and for
|
||||||
|
* maintaining consistency of usage of the HTTP "verb" at the client side
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String updateClientImage(@PathParam("clientId") final Long clientId, @HeaderParam("Content-Length") final Long fileSize,
|
||||||
|
@FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails,
|
||||||
|
@FormDataParam("file") final FormDataBodyPart bodyPart) {
|
||||||
|
return addNewClientImage(clientId, fileSize, inputStream, fileDetails, bodyPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is added only for consistency with other URL patterns and for
|
||||||
|
* maintaining consistency of usage of the HTTP "verb" at the client side
|
||||||
|
*
|
||||||
|
* Upload image as a Data URL (essentially a base64 encoded stream)
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String updateClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) {
|
||||||
|
return addNewClientImage(clientId, jsonRequestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
public String deleteClientImage(@PathParam("clientId") final Long clientId) {
|
||||||
|
this.clientWritePlatformService.deleteClientImage(clientId);
|
||||||
|
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is added only for consistency with other URL patterns and for
|
|
||||||
* maintaining consistency of usage of the HTTP "verb" at the client side
|
|
||||||
*
|
|
||||||
* Upload image as a Data URL (essentially a base64 encoded stream)
|
|
||||||
*/
|
|
||||||
@PUT
|
|
||||||
@Consumes({ MediaType.APPLICATION_JSON })
|
|
||||||
@Produces({ MediaType.APPLICATION_JSON })
|
|
||||||
public String updateClientImage(@PathParam("clientId") final Long clientId,
|
|
||||||
final String jsonRequestBody) {
|
|
||||||
return addNewClientImage(clientId, jsonRequestBody);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -5,9 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.client.exception;
|
package org.mifosplatform.portfolio.client.exception;
|
||||||
|
|
||||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
public class ImageNotFoundException extends AbstractPlatformDomainRuleException {
|
public class ImageNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
public ImageNotFoundException(final String resource, final Long resourceId) {
|
public ImageNotFoundException(final String resource, final Long resourceId) {
|
||||||
super("error.msg.entity.image.invalid", "Image for resource " + resource + " with Identifier " + resourceId + " does not exist",
|
super("error.msg.entity.image.invalid", "Image for resource " + resource + " with Identifier " + resourceId + " does not exist",
|
||||||
|
|||||||
+5
-5
@@ -23,21 +23,21 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
switch (loanEvent) {
|
switch (loanEvent) {
|
||||||
case LOAN_CREATED:
|
case LOAN_CREATED:
|
||||||
if (from == null) {
|
if (from == null) {
|
||||||
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL, allowedLoanStatuses);
|
newState = stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, allowedLoanStatuses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOAN_REJECTED:
|
case LOAN_REJECTED:
|
||||||
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
|
if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
|
||||||
newState = stateOf(LoanStatus.REJECTED, allowedLoanStatuses);
|
newState = stateOf(LoanStatus.REJECTED, allowedLoanStatuses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOAN_APPROVED:
|
case LOAN_APPROVED:
|
||||||
if (from.hasStateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL)) {
|
if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
|
||||||
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
|
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOAN_WITHDRAWN:
|
case LOAN_WITHDRAWN:
|
||||||
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.SUBMITED_AND_PENDING_APPROVAL, LoanStatus.APPROVED)) {
|
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED)) {
|
||||||
newState = stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, allowedLoanStatuses);
|
newState = stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, allowedLoanStatuses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -48,7 +48,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
break;
|
break;
|
||||||
case LOAN_APPROVAL_UNDO:
|
case LOAN_APPROVAL_UNDO:
|
||||||
if (from.hasStateOf(LoanStatus.APPROVED)) {
|
if (from.hasStateOf(LoanStatus.APPROVED)) {
|
||||||
newState = stateOf(LoanStatus.SUBMITED_AND_PENDING_APPROVAL, allowedLoanStatuses);
|
newState = stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, allowedLoanStatuses);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOAN_DISBURSAL_UNDO:
|
case LOAN_DISBURSAL_UNDO:
|
||||||
|
|||||||
+3
-3
@@ -11,7 +11,7 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
|||||||
public enum LoanStatus {
|
public enum LoanStatus {
|
||||||
|
|
||||||
INVALID(0, "loanStatusType.invalid"), //
|
INVALID(0, "loanStatusType.invalid"), //
|
||||||
SUBMITED_AND_PENDING_APPROVAL(100, "loanStatusType.submitted.and.pending.approval"), //
|
SUBMITTED_AND_PENDING_APPROVAL(100, "loanStatusType.submitted.and.pending.approval"), //
|
||||||
APPROVED(200, "loanStatusType.approved"), //
|
APPROVED(200, "loanStatusType.approved"), //
|
||||||
ACTIVE(300, "loanStatusType.active"), //
|
ACTIVE(300, "loanStatusType.active"), //
|
||||||
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
|
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
|
||||||
@@ -29,7 +29,7 @@ public enum LoanStatus {
|
|||||||
LoanStatus enumeration = LoanStatus.INVALID;
|
LoanStatus enumeration = LoanStatus.INVALID;
|
||||||
switch (statusValue) {
|
switch (statusValue) {
|
||||||
case 100:
|
case 100:
|
||||||
enumeration = LoanStatus.SUBMITED_AND_PENDING_APPROVAL;
|
enumeration = LoanStatus.SUBMITTED_AND_PENDING_APPROVAL;
|
||||||
break;
|
break;
|
||||||
case 200:
|
case 200:
|
||||||
enumeration = LoanStatus.APPROVED;
|
enumeration = LoanStatus.APPROVED;
|
||||||
@@ -77,7 +77,7 @@ public enum LoanStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSubmittedAndPendingApproval() {
|
public boolean isSubmittedAndPendingApproval() {
|
||||||
return this.value.equals(LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getValue());
|
return this.value.equals(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isApproved() {
|
public boolean isApproved() {
|
||||||
|
|||||||
+11
-5
@@ -26,6 +26,8 @@ import org.mifosplatform.portfolio.charge.domain.Charge;
|
|||||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeDeletedException.LOAN_CHARGE_CANNOT_BE_DELETED_REASON;
|
||||||
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException;
|
||||||
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedException.LOAN_CHARGE_CANNOT_BE_UPDATED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON;
|
||||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||||
@@ -529,6 +531,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Loan loan = retrieveLoanBy(loanId);
|
final Loan loan = retrieveLoanBy(loanId);
|
||||||
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||||
|
|
||||||
|
// Charges may be edited only when the loan associated with them are
|
||||||
|
// yet to be approved (are in submitted and pending status)
|
||||||
|
if (!loan.status().isSubmittedAndPendingApproval()) { throw new LoanChargeCannotBeUpdatedException(
|
||||||
|
LOAN_CHARGE_CANNOT_BE_UPDATED_REASON.LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE, loanCharge.getId()); }
|
||||||
|
|
||||||
final Map<String, Object> changes = loan.updateLoanCharge(loanCharge, command);
|
final Map<String, Object> changes = loan.updateLoanCharge(loanCharge, command);
|
||||||
|
|
||||||
this.loanRepository.save(loan);
|
this.loanRepository.save(loan);
|
||||||
@@ -597,11 +604,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Loan loan = retrieveLoanBy(loanId);
|
final Loan loan = retrieveLoanBy(loanId);
|
||||||
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||||
|
|
||||||
// validate loan charge is not partly paid or waived
|
// Charges may be deleted only when the loan associated with them are
|
||||||
if (loanCharge.isWaived()) {
|
// yet to be approved (are in submitted and pending status)
|
||||||
throw new LoanChargeCannotBeDeletedException(LOAN_CHARGE_CANNOT_BE_DELETED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
if (!loan.status().isSubmittedAndPendingApproval()) { throw new LoanChargeCannotBeDeletedException(
|
||||||
} else if (loanCharge.isPaidOrPartiallyPaid(loan.getCurrency())) { throw new LoanChargeCannotBeDeletedException(
|
LOAN_CHARGE_CANNOT_BE_DELETED_REASON.LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE, loanCharge.getId()); }
|
||||||
LOAN_CHARGE_CANNOT_BE_DELETED_REASON.ALREADY_PAID, loanCharge.getId()); }
|
|
||||||
|
|
||||||
loan.removeLoanCharge(loanCharge);
|
loan.removeLoanCharge(loanCharge);
|
||||||
this.loanRepository.save(loan);
|
this.loanRepository.save(loan);
|
||||||
|
|||||||
+3
-3
@@ -263,9 +263,9 @@ public class LoanEnumerations {
|
|||||||
case INVALID:
|
case INVALID:
|
||||||
optionData = new LoanStatusEnumData(LoanStatus.INVALID.getValue().longValue(), LoanStatus.INVALID.getCode(), "Invalid");
|
optionData = new LoanStatusEnumData(LoanStatus.INVALID.getValue().longValue(), LoanStatus.INVALID.getCode(), "Invalid");
|
||||||
break;
|
break;
|
||||||
case SUBMITED_AND_PENDING_APPROVAL:
|
case SUBMITTED_AND_PENDING_APPROVAL:
|
||||||
optionData = new LoanStatusEnumData(LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getValue().longValue(),
|
optionData = new LoanStatusEnumData(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue().longValue(),
|
||||||
LoanStatus.SUBMITED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval");
|
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval");
|
||||||
break;
|
break;
|
||||||
case APPROVED:
|
case APPROVED:
|
||||||
optionData = new LoanStatusEnumData(LoanStatus.APPROVED.getValue().longValue(), LoanStatus.APPROVED.getCode(), "Approved");
|
optionData = new LoanStatusEnumData(LoanStatus.APPROVED.getValue().longValue(), LoanStatus.APPROVED.getCode(), "Approved");
|
||||||
|
|||||||
Reference in New Issue
Block a user