You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by na...@apache.org on 2016/03/22 08:26:37 UTC
incubator-fineract git commit: Self Service - Loan Application
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 <ad...@confluxtechnologies.com>
Authored: Mon Mar 21 17:32:19 2016 +0530
Committer: Adi Narayana Raju <ad...@confluxtechnologies.com>
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"> </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"> </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"> </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"> </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"> </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"> </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"> </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();
}
}