Repository: incubator-fineract
Updated Branches:
  refs/heads/develop 04fe104dd -> 2ddea0aae


Self Service - Loan Application


Project: http://git-wip-us.apache.org/repos/asf/incubator-fineract/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-fineract/commit/2ddea0aa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-fineract/tree/2ddea0aa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-fineract/diff/2ddea0aa

Branch: refs/heads/develop
Commit: 2ddea0aaee8385591343026cef2f276765f72a79
Parents: 04fe104
Author: Adi Narayana Raju <[email protected]>
Authored: Mon Mar 21 17:32:19 2016 +0530
Committer: Adi Narayana Raju <[email protected]>
Committed: Mon Mar 21 17:32:19 2016 +0530

----------------------------------------------------------------------
 api-docs/apiLive.htm                            | 744 ++++++++++++++++++-
 .../loanaccount/api/SelfLoansApiResource.java   | 101 ++-
 .../data/SelfLoansDataValidator.java            |  75 +-
 .../useradministration/domain/AppUser.java      |   2 +-
 4 files changed, 914 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2ddea0aa/api-docs/apiLive.htm
----------------------------------------------------------------------
diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 4edd5e7..789582d 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -3159,10 +3159,42 @@
                                                                <td></td>
                                                        </tr>
                                                        <tr>
-                                                               <td><a 
href="#selfloan">Loans</a></td>
+                                                               <td><a 
href="#selfloantemplate">Loans</a></td>
+                                                               
<td>self/loans/template?templateType=individual</td>
+                                                               <td></td>
+                                                               <td><a 
href="#selfloantemplate">Loan Application Template</a></td>
+                                                               <td></td>
+                                                               <td></td>
+                                                       </tr>
+                                                       <tr>
+                                                               <td></td>
+                                                               
<td>self/loans?command=calculateLoanSchedule</td>
+                                                               <td><a 
href="#selfloancalc">Calculate Loan Repayment Schedule</a></td>
+                                                               <td></td>
+                                                               <td></td>
+                                                               <td></td>
+                                                       </tr>
+                                                       <tr>
+                                                               <td></td>
+                                                               
<td>self/loans</td>
+                                                               <td><a 
href="#selfloanapply">Submit new Loan Application</a></td>
+                                                               <td></td>
+                                                               <td></td>
+                                                               <td></td>
+                                                       </tr>
+                                                       <tr>
+                                                               <td></td>
                                                                
<td>self/loans/{loanId}</td>
                                                                <td></td>
                                                                <td><a 
href="#selfloan">Retrieve a Loan</a></td>
+                                                               <td><a 
href="#selfloanupdate">Update a Loan Application</a></td>
+                                                               <td></td>
+                                                       </tr>
+                                                       <tr>
+                                                               <td></td>
+                                                               
<td>self/loans/{loanId}?command=withdrawnByApplicant</td>
+                                                               <td><a 
href="#selfloanwithdraw">Withdraw Loan Application</a></td>
+                                                               <td></td>
                                                                <td></td>
                                                                <td></td>
                                                        </tr>
@@ -39210,10 +39242,10 @@ No Request Body
                                </div>
                                <div class="method-example">
                                        <code class="method-declaration">
-POST https://DomainName/api/v1/userdetails?access_token={access_token}
+POST https://DomainName/api/v1/self/userdetails?access_token={access_token}
                                        </code>
                                        <code class="method-request">
-POST userdetails?access_token=bWlmb3M6cGFzc3dvcmQ=
+POST self/userdetails?access_token=bWlmb3M6cGFzc3dvcmQ=
 Content-Type: application/json
 No Request Body
                                        </code>
@@ -39989,6 +40021,610 @@ GET 
https://DomainName/api/v1/self/clients/{clientId}/transaction/{transactionId
                                </div>
                        </div>
 
+                       <a id="selfloantemplate" name="selfloantemplate" 
class="old-syle-anchor">&nbsp;</a>
+                       <div class="method-section">
+                               <div class="method-description">
+                                       <h4>Retrieve Loan Details Template</h4>
+                                       <p>This is a convenience resource. It 
can be useful when
+                                               building maintenance user 
interface screens for client
+                                               applications. The template data 
returned consists of any or all
+                                               of:
+                                       <ul>
+                                               <li class=normalli>Field 
Defaults</li>
+                                               <li class=normalli>Allowed 
Value Lists</li>
+                                       </ul>
+                                       </p>
+                                       <h5>Arguments</h5>
+                                       <dl class="argument-list">
+                                               <dt>templateType</dt>
+                                               <dd>
+                                                       String 
<span>mandatory</span>, only allowed value is <span>individual</span>
+                                               </dd>
+                                               <dd>
+                                                       <br>templateType value 
decides the required template data for creating a new loan application.
+
+                                               </dd>
+                                               <dd>
+                                                       
<br><b>'individual':</b> Loan template data for creating individual loans.
+                                               </dd>
+                                               <dt>clientId</dt>
+                                               <dd>
+                                                       Integer 
<span>mandatory</span>
+                                               </dd>
+                                       </dl>
+                                       <h5>Optional Arguments</h5>
+                                       <dl class="argument-list">
+                                               <dt>productId</dt>
+                                               <dd>
+                                                       Integer 
<span>optional</span>
+                                               </dd>
+                                               <dd>If entered, productId, 
productName and selectedProduct
+                                                       fields are 
returned.</dd>
+
+                                       </dl>
+                                       <p>Example Requests:</p>
+                                       <div 
class=apiClick>self/loans/template?templateType=individual&clientId=1</div>
+                                       <br>
+                                       <br>
+                                       <div 
class=apiClick>self/loans/template?templateType=individual&clientId=1&productId=1</div>
+                               </div>
+                               <div class="method-example">
+                                       <code class="method-declaration">
+GET 
https://DomainName/api/v1/self/loans/template?templateType=individual&clientId=1
+                                       </code>
+                                       <code class="method-response">
+{
+  "clientId": 1,
+  "clientName": "Kampala first Client",
+  "clientOfficeId": 2,
+  "timeline": {
+    "expectedDisbursementDate": [
+      2013,
+      3,
+      8
+    ]
+  },
+  "productOptions": [
+    {
+      "id": 1,
+      "name": "Kampala Product (with cash accounting)"
+    }
+  ]
+}
+                                       </code>
+                                       <code class="method-declaration">
+GET 
https://DomainName/api/v1/self/loans/template?templateType=individual&clientId=1&productId=1
+                                       </code>
+                                       <code class="method-response">
+{
+  "clientId": 1,
+  "clientName": "Kampala first Client",
+  "clientOfficeId": 2,
+  "loanProductId": 1,
+  "loanProductName": "Kampala Product (with cash accounting)",
+  "loanProductDescription": "Typical Kampala loan product with cash accounting 
enabled for testing.",
+  "currency": {
+    "code": "UGX",
+    "name": "Uganda Shilling",
+    "decimalPlaces": 2,
+    "displaySymbol": "USh",
+    "nameCode": "currency.UGX",
+    "displayLabel": "Uganda Shilling (USh)"
+  },
+  "principal": 1000000,
+  "termFrequency": 12,
+  "termPeriodFrequencyType": {
+    "id": 2,
+    "code": "repaymentFrequency.periodFrequencyType.months",
+    "value": "Months"
+  },
+  "numberOfRepayments": 12,
+  "repaymentEvery": 1,
+  "repaymentFrequencyType": {
+    "id": 2,
+    "code": "repaymentFrequency.periodFrequencyType.months",
+    "value": "Months"
+  },
+  "interestRatePerPeriod": 24,
+  "interestRateFrequencyType": {
+    "id": 3,
+    "code": "interestRateFrequency.periodFrequencyType.years",
+    "value": "Per year"
+  },
+  "annualInterestRate": 24,
+  "amortizationType": {
+    "id": 1,
+    "code": "amortizationType.equal.installments",
+    "value": "Equal installments"
+  },
+  "interestType": {
+    "id": 1,
+    "code": "interestType.flat",
+    "value": "Flat"
+  },
+  "interestCalculationPeriodType": {
+    "id": 1,
+    "code": "interestCalculationPeriodType.same.as.repayment.period",
+    "value": "Same as repayment period"
+  },
+  "transactionProcessingStrategyId": 2,
+  "timeline": {
+    "expectedDisbursementDate": [
+      2013,
+      3,
+      8
+    ]
+  },
+  "daysInMonthType": {
+    "id": 30,
+    "code": "DaysInMonthType.days360",
+    "value": "30 Days"
+  },
+  "daysInYearType": {
+       "id": 360,
+       "code": "DaysInYearType.days360",
+       "value": "360 Days"
+  },
+  "isInterestRecalculationEnabled": true,
+  "interestRecalculationData": {
+    "interestRecalculationCompoundingType": {
+      "id": 2,
+      "code": "interestRecalculationCompoundingMethod.fee",
+      "value": "Fee"
+    },
+       "recalculationCompoundingFrequencyType": {
+                   "id":1,
+                       
"code":"interestRecalculationFrequencyType.same.as.repayment.period",
+                       "value":"Same as repayment period"
+       },
+    "rescheduleStrategyType": {
+      "id": 2,
+      "code": "loanRescheduleStrategyMethod.reduce.number.of.installments",
+      "value": "Reduce number of installments"
+    },
+       "recalculationRestFrequencyType": {
+                   "id":1,
+                       
"code":"interestRecalculationFrequencyType.same.as.repayment.period",
+                       "value":"Same as repayment period"
+       }
+  }
+  "charges": [],
+  "productOptions": [
+    {
+      "id": 1,
+      "name": "Kampala Product (with cash accounting)"
+    }
+  ],
+  "loanOfficerOptions": [
+    {
+      "id": 2,
+      "firstname": "Kampala",
+      "lastname": "LoanOfficer",
+      "displayName": "LoanOfficer, Kampala",
+      "officeId": 2,
+      "officeName": "Uganda (Kampala)",
+      "isLoanOfficer": true
+    }
+  ],
+  "loanPurposeOptions": [
+    {
+      "id": 20,
+      "name": "option.Agriculture",
+      "position": 1
+    },
+    {
+      "id": 21,
+      "name": "option.Manufacturing",
+      "position": 20
+    },
+    {
+      "id": 22,
+      "name": "option.HousingImprovement",
+      "position": 21
+    }
+  ],
+  "termFrequencyTypeOptions": [
+    {
+      "id": 0,
+      "code": "loanTermFrequency.periodFrequencyType.days",
+      "value": "Days"
+    },
+    {
+      "id": 1,
+      "code": "loanTermFrequency.periodFrequencyType.weeks",
+      "value": "Weeks"
+    },
+    {
+      "id": 2,
+      "code": "loanTermFrequency.periodFrequencyType.months",
+      "value": "Months"
+    },
+    {
+      "id": 3,
+      "code": "loanTermFrequency.periodFrequencyType.years",
+      "value": "Years"
+    }
+  ],
+  "repaymentFrequencyTypeOptions": [
+    {
+      "id": 0,
+      "code": "repaymentFrequency.periodFrequencyType.days",
+      "value": "Days"
+    },
+    {
+      "id": 1,
+      "code": "repaymentFrequency.periodFrequencyType.weeks",
+      "value": "Weeks"
+    },
+    {
+      "id": 2,
+      "code": "repaymentFrequency.periodFrequencyType.months",
+      "value": "Months"
+    }
+  ],
+  "interestRateFrequencyTypeOptions": [
+    {
+      "id": 2,
+      "code": "interestRateFrequency.periodFrequencyType.months",
+      "value": "Per month"
+    },
+    {
+      "id": 3,
+      "code": "interestRateFrequency.periodFrequencyType.years",
+      "value": "Per year"
+    }
+  ],
+  "amortizationTypeOptions": [
+    {
+      "id": 1,
+      "code": "amortizationType.equal.installments",
+      "value": "Equal installments"
+    },
+    {
+      "id": 0,
+      "code": "amortizationType.equal.principal",
+      "value": "Equal principle payments"
+    }
+  ],
+  "interestTypeOptions": [
+    {
+      "id": 1,
+      "code": "interestType.flat",
+      "value": "Flat"
+    },
+    {
+      "id": 0,
+      "code": "interestType.declining.balance",
+      "value": "Declining Balance"
+    }
+  ],
+  "interestCalculationPeriodTypeOptions": [
+    {
+      "id": 0,
+      "code": "interestCalculationPeriodType.daily",
+      "value": "Daily"
+    },
+    {
+      "id": 1,
+      "code": "interestCalculationPeriodType.same.as.repayment.period",
+      "value": "Same as repayment period"
+    }
+  ],
+  "transactionProcessingStrategyOptions": [
+    {
+      "id": 2,
+      "code": "heavensfamily-strategy",
+      "name": "Heavensfamily"
+    }
+  ],
+  "chargeOptions": [
+    {
+      "id": 1,
+      "name": "Bank Fee (per installment)",
+      "active": true,
+      "penalty": false,
+      "currency": {
+        "code": "UGX",
+        "name": "Uganda Shilling",
+        "decimalPlaces": 2,
+        "displaySymbol": "USh",
+        "nameCode": "currency.UGX",
+        "displayLabel": "Uganda Shilling (USh)"
+      },
+      "amount": 1500,
+      "chargeTimeType": {
+        "id": 2,
+        "code": "chargeTimeType.specifiedDueDate",
+        "value": "Specified due date"
+      },
+      "chargeAppliesTo": {
+        "id": 1,
+        "code": "chargeAppliesTo.loan",
+        "value": "Loan"
+      },
+      "chargeCalculationType": {
+        "id": 1,
+        "code": "chargeCalculationType.flat",
+        "value": "Flat"
+      }
+    }
+  ],
+  "loanCollateralOptions": [
+    {
+      "id": 17,
+      "name": "option.House",
+      "position": 1
+    },
+    {
+      "id": 18,
+      "name": "option.Television",
+      "position": 17
+    },
+    {
+      "id": 19,
+      "name": "option.Gold",
+      "position": 18
+    }
+  ],
+  "accountLinkingOptions":[
+       {
+               "id":1,
+               "accountNo":"000000001",
+               "clientId":1,
+               "clientName":"pramod nuthakki",
+               "productId":1,
+               "productName":"pramod sav",
+               "fieldOfficerId":0,
+               "currency":{"code":"USD","name":"US 
Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US
 Dollar ($)"}
+       }
+  ]
+}
+                                       </code>
+                               </div>
+                       </div>
+
+                       <a id="selfloancalc" name="selfloancalc" 
class="old-syle-anchor">&nbsp;</a>
+                       <div class="method-section">
+                               <div class="method-description">
+                                       <h4>Calculate Loan Repayment 
Schedule</h4>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Mandatory Fields</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       <td>productId, 
principal, loanTermFrequency,
+                                                               
loanTermFrequencyType, numberOfRepayments, repaymentEvery,
+                                                               
repaymentFrequencyType, interestRatePerPeriod,
+                                                               
amortizationType, interestType,
+                                                               
interestCalculationPeriodType, expectedDisbursementDate,
+                                                               
transactionProcessingStrategyId</td>
+                                               </tr>
+                                       </table>
+                               </div>
+                               <div class="method-example">
+                                       <code class="method-declaration">
+POST https://DomainName/api/v1/self/loans?command=calculateLoanSchedule
+                                       </code>
+                                       <code class="method-request">
+POST self/loans?command=calculateLoanSchedule
+Content-Type: application/json
+Request Body:
+{
+    "dateFormat": "dd MMMM yyyy",
+    "locale": "en_GB",
+    "productId": 1,
+    "principal": "100,000.00",
+    "loanTermFrequency": 12,
+    "loanTermFrequencyType": 2,
+    "numberOfRepayments": 12,
+    "repaymentEvery": 1,
+    "repaymentFrequencyType": 2,
+    "interestRatePerPeriod": 2,
+    "amortizationType": 1,
+    "interestType": 0,
+    "interestCalculationPeriodType": 1,
+    "expectedDisbursementDate": "20 September 2011",
+    "transactionProcessingStrategyId": 2
+}
+                                       </code>
+                                       <code class="method-response">
+{
+  "currency": {
+    "code": "UGX",
+    "name": "Uganda Shilling",
+    "decimalPlaces": 2,
+    "displaySymbol": "USh",
+    "nameCode": "currency.UGX",
+    "displayLabel": "Uganda Shilling (USh)"
+  },
+  "loanTermInDays": 366,
+  "totalPrincipalDisbursed": 100000,
+  "totalPrincipalExpected": 100000,
+  "totalPrincipalPaid": 0,
+  "totalInterestCharged": 13471.52,
+  "totalFeeChargesCharged": 0,
+  "totalPenaltyChargesCharged": 0,
+  "totalWaived": 0,
+  "totalWrittenOff": 0,
+  "totalRepaymentExpected": 113471.52,
+  "totalRepayment": 0,
+  "totalOutstanding": 0,
+  "periods": [
+    {
+      "period": 0,
+      "dueDate": [
+        2011,
+        9,
+        20
+      ],
+      "principalDisbursed": 100000,
+      "principalLoanBalanceOutstanding": 100000,
+      "feeChargesDue": 0,
+      "feeChargesOutstanding": 0,
+      "totalOriginalDueForPeriod": 0,
+      "totalDueForPeriod": 0,
+      "totalOutstandingForPeriod": 0,
+      "totalOverdue": 0,
+      "totalActualCostOfLoanForPeriod": 0
+    },
+    {
+      "period": 1,
+      "fromDate": [
+        2011,
+        9,
+        20
+      ],
+      "dueDate": [
+        2011,
+        10,
+        20
+      ],
+      "daysInPeriod": 30,
+      "principalOriginalDue": 7455.96,
+      "principalDue": 7455.96,
+      "principalOutstanding": 7455.96,
+      "principalLoanBalanceOutstanding": 92544.04,
+      "interestOriginalDue": 2000,
+      "interestDue": 2000,
+      "interestOutstanding": 2000,
+      "feeChargesDue": 0,
+      "penaltyChargesDue": 0,
+      "totalOriginalDueForPeriod": 9455.96,
+      "totalDueForPeriod": 9455.96,
+      "totalPaidForPeriod": 0,
+      "totalOutstandingForPeriod": 9455.96,
+      "totalOverdue": 9455.96,
+      "totalActualCostOfLoanForPeriod": 2000
+    },
+    ...
+    ...
+    {
+      "period": 12,
+      "fromDate": [
+        2012,
+        8,
+        20
+      ],
+      "dueDate": [
+        2012,
+        9,
+        20
+      ],
+      "daysInPeriod": 31,
+      "principalOriginalDue": 9270.56,
+      "principalDue": 9270.56,
+      "principalOutstanding": 9270.56,
+      "principalLoanBalanceOutstanding": 0,
+      "interestOriginalDue": 185.4,
+      "interestDue": 185.4,
+      "interestOutstanding": 185.4,
+      "feeChargesDue": 0,
+      "penaltyChargesDue": 0,
+      "totalOriginalDueForPeriod": 9455.96,
+      "totalDueForPeriod": 9455.96,
+      "totalPaidForPeriod": 0,
+      "totalOutstandingForPeriod": 9455.96,
+      "totalOverdue": 9455.96,
+      "totalActualCostOfLoanForPeriod": 185.4
+    }
+  ]
+}
+                                       </code>
+                               </div>
+                       </div>
+                       
+                       
+                       <a id="selfloanapply" name="selfloanapply" 
class="old-syle-anchor">&nbsp;</a>
+                       <div class="method-section">
+                               <div class="method-description">
+                                       <h4>Submit a new Loan Application</h4>
+                                       <p>Only individual loanType can be used 
by Self Service User </p>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Mandatory Fields</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       <td>clientId, 
productId, principal, loanTermFrequency,
+                                                               
loanTermFrequencyType, loanType, numberOfRepayments, repaymentEvery,
+                                                               
repaymentFrequencyType, interestRatePerPeriod,
+                                                               
amortizationType, interestType,
+                                                               
interestCalculationPeriodType, transactionProcessingStrategyId,
+                                                               
expectedDisbursementDate, submittedOnDate, loanType</td>
+                                               </tr>
+                                       </table>
+                                       <br/>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Additional Mandatory Fields if interest recalculation 
is enabled for product and Rest frequency  not same as repayment 
period</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       
<td>recalculationRestFrequencyDate</td>
+                                               </tr>
+                                       </table>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Additional Mandatory Fields if interest recalculation 
with interest/fee compounding is enabled for product and compounding frequency 
not same as repayment period</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       
<td>recalculationCompoundingFrequencyDate</td>
+                                               </tr>
+                                       </table>
+                                       <br/>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Optional Fields</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       
<td>graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, 
linkAccountId, allowPartialPeriodInterestCalcualtion,
+                                                                
fixedEmiAmount, maxOutstandingLoanBalance, disbursementData, 
graceOnArrearsAgeing, createStandingInstructionAtDisbursement (requires 
linkedAccountId if set to true)
+                                                       </td>
+                                               </tr>
+                                       </table>
+                               </div>
+                               <div class="method-example">
+                                       <code class="method-declaration">
+POST https://DomainName/api/v1/self/loans
+                                       </code>
+                                       <code class="method-request">
+POST self/loans
+Content-Type: application/json
+Request Body:
+{
+    "dateFormat": "dd MMMM yyyy",
+    "locale": "en_GB",
+    "clientId": 1,
+    "productId": 1,
+    "principal": "10,000.00",
+    "loanTermFrequency": 12,
+    "loanTermFrequencyType": 2,
+    "loanType": "individual",
+    "numberOfRepayments": 10,
+    "repaymentEvery": 1,
+    "repaymentFrequencyType": 2,
+    "interestRatePerPeriod": 10,
+    "amortizationType": 1,
+    "interestType": 0,
+    "interestCalculationPeriodType": 1,
+    "transactionProcessingStrategyId": 1,
+    "expectedDisbursementDate": "10 Jun 2013",
+    "submittedOnDate": "10 Jun 2013",
+    "linkAccountId" : "1",
+    "fixedEmiAmount":1100,
+    "maxOutstandingLoanBalance":"35000",
+    "disbursementData":[{"expectedDisbursementDate":"01 November 2013",
+                                       
"principal":22000,"approvedPrincipal":22000}]
+}
+                                       </code>
+                                       <code class="method-response">
+{
+    "officeId": 1,
+    "clientId": 1,
+    "loanId": 1,
+    "resourceId": 1
+}
+                                       </code>
+                               </div>
+                       </div>
                        <a id="selfloan" name="selfloan" 
class="old-syle-anchor">&nbsp;</a>
                        <div class="method-section">
                                <div class="method-description">
@@ -40265,6 +40901,108 @@ GET https://DomainName/api/v1/self/loans/{loanId}
                                </div>
                        </div>
 
+                       <a id="selfloanupdate" name="selfloanupdate" 
class="old-syle-anchor">&nbsp;</a>
+                       <div class="method-section">
+                               <div class="method-description">
+                                       <h2>Update a Loan Application</h2>
+                                       <p>Loan application can only be 
modified when in 'Submitted and pending approval' state. Once the application 
is approved, the details cannot be changed using this method.</p>
+                               </div>
+                               <div class="method-example">
+                                       <code class="method-declaration">PUT 
https://Domain Name/api/v1/self/loans/{loanId}</code>
+                                       <code class="method-request">PUT 
self/loans/1
+Content-Type: application/json
+No Request Body:
+{
+  "locale": "en",
+  "dateFormat": "dd MMMM yyyy",
+  "productId": 1,
+  "principal": "5000",
+  "loanTermFrequency": 10,
+  "loanTermFrequencyType": 0,
+  "numberOfRepayments": 10,
+  "repaymentEvery": 1,
+  "repaymentFrequencyType": 0,
+  "interestRatePerPeriod": 2,
+  "interestType": 0,
+  "interestCalculationPeriodType": 0,
+  "amortizationType": 1,
+  "expectedDisbursementDate": "04 March 2014",
+  "transactionProcessingStrategyId": 1
+}
+                                       </code>
+                                       <code class="method-response">
+{
+  "officeId": 2,
+  "clientId": 1,
+  "loanId": 1,
+  "resourceId": 1,
+  "changes": {
+    "principal": 5000,
+    "locale": "en"
+  }
+}
+                                       </code>
+                               </div>
+                       </div>
+
+                       <a id="selfloanwithdraw" name="selfloanwithdraw" 
class="old-syle-anchor">&nbsp;</a>
+                       <div class="method-section">
+                               <div class="method-description">
+                                       <h4>Applicant Withdraws from Loan 
Application</h4>
+                                       <table class=matrixHeading>
+                                               <tr class="matrixHeadingBG">
+                                                       <td><div 
class="fineractHeading2">Mandatory Fields</div></td>
+                                               </tr>
+                                               <tr class=alt>
+                                                       <td>withdrawnOnDate</td>
+                                               </tr>
+                                       </table>
+                               </div>
+                               <div class="method-example">
+                                       <code class="method-declaration">
+POST https://DomainName/api/v1/self/loans/{loanId}?command=withdrawnByApplicant
+                                       </code>
+                                       <code class="method-request">
+POST self/loans/1?command=withdrawnByApplicant
+Content-Type: application/json
+Request Body:
+{
+    "locale": "en",
+    "dateFormat": "dd MMMM yyyy",
+    "withdrawnOnDate": "20 September 2011",
+    "note": "Reason loan applicant withdrew from application."
+}
+</code>
+                                       <code class="method-response">
+{
+  "officeId": 1,
+  "clientId": 1,
+  "loanId": 2,
+  "resourceId": 2,
+  "changes": {
+    "status": {
+      "id": 400,
+      "code": "loanStatusType.withdrawn.by.client",
+      "value": "Withdrawn by applicant",
+      "pendingApproval": false,
+      "waitingForDisbursal": false,
+      "active": false,
+      "closedObligationsMet": false,
+      "closedWrittenOff": false,
+      "closedRescheduled": false,
+      "closed": false,
+      "overpaid": false
+    },
+    "locale": "en",
+    "dateFormat": "dd MMMM yyyy",
+    "withdrawnOnDate": "20 September 2011",
+    "closedOnDate": "20 September 2011"
+  }
+}
+                                       </code>
+                               </div>
+                       </div>
+
             <a id="selfloantransaction" name="selfloantransaction" 
class="old-syle-anchor">&nbsp;</a>
                        <div class="method-section">
                                <div class="method-description">

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2ddea0aa/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java
index d317b5b..7281752 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/api/SelfLoansApiResource.java
@@ -18,20 +18,31 @@
  */
 package org.apache.fineract.portfolio.self.loanaccount.api;
 
+import java.util.HashMap;
+
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 
+import org.apache.commons.lang.StringUtils;
+import 
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource;
 import 
org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource;
 import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
+import 
org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException;
+import 
org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException;
+import 
org.apache.fineract.portfolio.self.client.service.AppuserClientMapperReadService;
 import 
org.apache.fineract.portfolio.self.loanaccount.data.SelfLoansDataValidator;
 import 
org.apache.fineract.portfolio.self.loanaccount.service.AppuserLoansMapperReadService;
 import org.apache.fineract.useradministration.domain.AppUser;
@@ -49,6 +60,7 @@ public class SelfLoansApiResource {
        private final LoanTransactionsApiResource loanTransactionsApiResource;
        private final LoanChargesApiResource loanChargesApiResource;
        private final AppuserLoansMapperReadService 
appuserLoansMapperReadService;
+       private final AppuserClientMapperReadService 
appUserClientMapperReadService;
        private final SelfLoansDataValidator dataValidator;
 
        @Autowired
@@ -57,12 +69,14 @@ public class SelfLoansApiResource {
                        final LoanTransactionsApiResource 
loanTransactionsApiResource,
                        final LoanChargesApiResource loanChargesApiResource,
                        final AppuserLoansMapperReadService 
appuserLoansMapperReadService,
+                       final AppuserClientMapperReadService 
appUserClientMapperReadService,
                        final SelfLoansDataValidator dataValidator) {
                this.context = context;
                this.loansApiResource = loansApiResource;
                this.loanTransactionsApiResource = loanTransactionsApiResource;
                this.loanChargesApiResource = loanChargesApiResource;
                this.appuserLoansMapperReadService = 
appuserLoansMapperReadService;
+               this.appUserClientMapperReadService = 
appUserClientMapperReadService;
                this.dataValidator = dataValidator;
        }
 
@@ -125,7 +139,79 @@ public class SelfLoansApiResource {
                return this.retrieveLoanCharge(loanId, loanChargeId, uriInfo);
        }
 
-       private void validateAppuserLoanMapping(final Long loanId) {
+    @GET
+    @Path("template")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String template(@QueryParam("clientId") final Long clientId, 
+               @QueryParam("productId") final Long productId,
+               @QueryParam("templateType") final String templateType,
+               @Context final UriInfo uriInfo) {
+       
+       if(clientId != null){
+               validateAppuserClientsMapping(clientId);
+       }
+       
+        if (templateType == null) {
+            final String errorMsg = "Loan template type must be provided";
+            throw new LoanTemplateTypeRequiredException(errorMsg);
+        } else if (!(templateType.equalsIgnoreCase("individual") 
+                       || templateType.equalsIgnoreCase("collateral"))){
+            final String errorMsg = "Loan template type '" + templateType + "' 
is not supported";
+            throw new NotSupportedLoanTemplateTypeException(errorMsg, 
templateType);
+        }
+       final Long groupId = null;
+       final boolean staffInSelectedOfficeOnly = false;
+       final boolean onlyActive = true;
+       return this.loansApiResource.template(clientId, groupId, productId, 
+                       templateType, staffInSelectedOfficeOnly, onlyActive, 
uriInfo);
+
+    }
+    
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String 
calculateLoanScheduleOrSubmitLoanApplication(@QueryParam("command") final 
String commandParam,
+            @Context final UriInfo uriInfo, final String apiRequestBodyAsJson) 
{
+       
+       HashMap<String, Object> attr = 
this.dataValidator.validateLoanApplication(apiRequestBodyAsJson);
+        final Long clientId = (Long) attr.get("clientId");
+        validateAppuserClientsMapping(clientId);
+
+       return 
this.loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(commandParam,
 
+                       uriInfo, apiRequestBodyAsJson);
+    }
+    
+    @PUT
+    @Path("{loanId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String modifyLoanApplication(@PathParam("loanId") final Long 
loanId, final String apiRequestBodyAsJson) {
+
+       HashMap<String, Object> attr = 
this.dataValidator.validateModifyLoanApplication(apiRequestBodyAsJson);
+        validateAppuserLoanMapping(loanId);
+        final Long clientId = (Long) attr.get("clientId");
+        if(clientId != null){
+            validateAppuserClientsMapping(clientId);
+        }
+
+       return this.loansApiResource.modifyLoanApplication(loanId, 
apiRequestBodyAsJson);
+    }
+    
+    @POST
+    @Path("{loanId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String stateTransitions(@PathParam("loanId") final Long loanId, 
@QueryParam("command") final String commandParam,
+            final String apiRequestBodyAsJson) {
+       if (!is(commandParam, "withdrawnByApplicant")) {
+               throw new UnrecognizedQueryParamException("command", 
commandParam);
+       }
+       validateAppuserLoanMapping(loanId);
+       return this.loansApiResource.stateTransitions(loanId, commandParam, 
apiRequestBodyAsJson);
+    }
+    
+    private void validateAppuserLoanMapping(final Long loanId) {
                AppUser user = this.context.authenticatedUser();
                final boolean isLoanMappedToUser = 
this.appuserLoansMapperReadService
                                .isLoanMappedToUser(loanId, user.getId());
@@ -133,5 +219,18 @@ public class SelfLoansApiResource {
                        throw new LoanNotFoundException(loanId);
                }
        }
+    
+       private void validateAppuserClientsMapping(final Long clientId) {
+               AppUser user = this.context.authenticatedUser();
+               final boolean mappedClientId = 
this.appUserClientMapperReadService
+                               .isClientMappedToUser(clientId, user.getId());
+               if (!mappedClientId) {
+                       throw new ClientNotFoundException(clientId);
+               }
+       }
+
+    private boolean is(final String commandParam, final String commandValue) {
+        return StringUtils.isNotBlank(commandParam) && 
commandParam.trim().equalsIgnoreCase(commandValue);
+    }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2ddea0aa/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/data/SelfLoansDataValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/data/SelfLoansDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/data/SelfLoansDataValidator.java
index 7560fba..ef93de0 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/data/SelfLoansDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/loanaccount/data/SelfLoansDataValidator.java
@@ -20,16 +20,26 @@ package org.apache.fineract.portfolio.self.loanaccount.data;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import javax.ws.rs.core.UriInfo;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import com.google.gson.JsonElement;
+
 @Component
 public class SelfLoansDataValidator {
        private static final Set<String> allowedAssociationParameters = new 
HashSet<>(
@@ -37,12 +47,16 @@ public class SelfLoansDataValidator {
                                        "originalSchedule", "transactions", 
"charges",
                                        "guarantors", "collateral", 
"linkedAccount",
                                        "multiDisburseDetails"));
-
+       private final FromJsonHelper fromApiJsonHelper;
+       
+       @Autowired
+       public SelfLoansDataValidator(final FromJsonHelper fromApiJsonHelper){
+               this.fromApiJsonHelper = fromApiJsonHelper;
+       }
+       
        public void validateRetrieveLoan(final UriInfo uriInfo) {
                List<String> unsupportedParams = new ArrayList<>();
 
-               validateTemplate(uriInfo, unsupportedParams);
-
                Set<String> associationParameters = ApiParameterHelper
                                
.extractAssociationsForResponseIfProvided(uriInfo
                                                .getQueryParameters());
@@ -68,6 +82,60 @@ public class SelfLoansDataValidator {
                throwExceptionIfReqd(unsupportedParams);
 
        }
+       
+       public HashMap<String, Object> validateLoanApplication(final String 
json){
+        if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("loan");
+
+        final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+        final String loanTypeParameterName = "loanType";
+        final String loanTypeStr = 
this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
+        
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull().equals("individual");
+
+        final String clientIdParameterName = "clientId";
+        final String clientId = 
this.fromApiJsonHelper.extractStringNamed(clientIdParameterName, element);
+        
baseDataValidator.reset().parameter(clientIdParameterName).value(clientId).notNull().longGreaterThanZero();
+
+        if (!dataValidationErrors.isEmpty()) { throw new 
PlatformApiDataValidationException(dataValidationErrors); }
+        
+        HashMap<String, Object> retAttr = new HashMap<>();
+        retAttr.put("clientId", Long.parseLong(clientId));
+        
+        return retAttr;
+
+       }
+
+       public HashMap<String, Object> validateModifyLoanApplication(final 
String json) {
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("loan");
+
+        final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+        final String loanTypeParameterName = "loanType";
+        if(this.fromApiJsonHelper.parameterExists(loanTypeParameterName, 
element)){
+            final String loanTypeStr = 
this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
+            
baseDataValidator.reset().parameter(loanTypeParameterName).value(loanTypeStr).notNull().equals("individual");
+        }
+
+        final String clientIdParameterName = "clientId";
+        String clientId = null;
+        if(this.fromApiJsonHelper.parameterExists(clientIdParameterName, 
element)){
+            clientId = 
this.fromApiJsonHelper.extractStringNamed(clientIdParameterName, element);
+            
baseDataValidator.reset().parameter(clientIdParameterName).value(clientId).notNull().longGreaterThanZero();
+        }
+
+        if (!dataValidationErrors.isEmpty()) { throw new 
PlatformApiDataValidationException(dataValidationErrors); }
+        
+        HashMap<String, Object> retAttr = new HashMap<>();
+        if(clientId != null){
+            retAttr.put("clientId", Long.parseLong(clientId));
+        }
+        
+        return retAttr;
+       }
 
        private void throwExceptionIfReqd(List<String> unsupportedParams) {
                if (unsupportedParams.size() > 0) {
@@ -84,4 +152,5 @@ public class SelfLoansDataValidator {
                }
        }
 
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/2ddea0aa/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java
 
b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java
index b68f794..7bf9558 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUser.java
@@ -325,7 +325,7 @@ public class AppUser extends AbstractPersistable<Long> 
implements PlatformUser {
         }else if(!this.isSelfServiceUser && 
actualChanges.containsKey(AppUserConstants.IS_SELF_SERVICE_USER)){
                actualChanges.put(AppUserConstants.CLIENTS, new ArrayList<>());
                if(this.appUserClientMappings != null){
-                       this.appUserClientMappings = null;
+                       this.appUserClientMappings.clear();
                }
         }
 


Reply via email to