diff --git a/CHANGELOG.md b/CHANGELOG.md
index a03caaa8b..9876714a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for
Releases
===============
+1. 02 Dec 2015 - 15.12.1.RELEASE
1. 18 Nov 2015 - 15.11.2.RELEASE
1. 04 Nov 2015 - 15.11.1.RELEASE
1. 21 Oct 2015 - 15.10.2.RELEASE
@@ -80,6 +81,36 @@ Releases
1. 05 April 2013 - 0.11.1.beta
1. 05 April 2013 - 0.11.0.beta
+15.12.1.RELEASE
+=============
+This release includes first part of Social Performance Management Framework.
+This release also includes few bug fixes as mentioned below.
+
+Platform & API
+
+New Features & Improvements
+Following stories of Social Performance Management Framework Epic
+ - [MIFOSX-1961] - Create new survey
+ - [MIFOSX-1962] - Add general information
+ - [MIFOSX-1963] - Add questions to a survey
+ - [MIFOSX-1964] - Add look-up tables
+ - [MIFOSX-1965] - Assign default look-up table
+
+Bugs
+ - [MIFOSX-2184] - As a user I want my penalties only to apply to new loans that go overdue
+ - [MIFOSX-2313] - Loan provisioning entries not getting created for multi tranche loans if first tranche disbursement date is back dated
+ - [MIFOSX-2322] - Template-User defined report is not working as expected
+ - [MIFOSX-2348] - incorrect error Insufficient account balance due to guarantors funds onhold
+ - [MIFOSX-2363] - Repayment schedule generated after early repayment with interest recalculation is not proper
+ - [MIFOSX-2368] - For the Multi tranche Loans with Equal Principal payments, last installment is not calculating properly
+
+Community-App
+
+Bugs
+ - [MIFOSX-2058] - Opening Screen for Non-Admin Users
+ - [MIFOSX-2327] - Does not come back to group, when cancel transfer clients
+ - [MIFOSX-2362] - Not able to save modify loan aplication after undo disburse and undo approval
+
15.11.2.RELEASE
=============
This release introduces floating rates feature.
diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 3e00b3122..b5e40ca60 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -90,7 +90,7 @@
@@ -2912,6 +2912,99 @@
+
+
+
+
+
+
@@ -3124,7 +3217,7 @@
Default authentication is using HTTP Basic Auth. Oauth2 can be enabled by using -Psecurity=oauth option on gradle build command , refer
- the platform setup wiki for additional details.
+ the platform setup wiki for additional details.
The platform has been configured to reject plain HTTP requests and
@@ -3253,7 +3346,7 @@ function executeAjaxRequest(url, verbType, jsonData, authKey, successFunction, e
-
+
@@ -5731,7 +5824,7 @@ Request Body:
POST collectionsheet?command=generateCollectionSheet
Content-Type: application/json
Request Body:
-{
+{
"dateFormat":"dd MMMM yyyy",
"locale":"en",
"transactionDate":"15 January 2015",
@@ -5812,7 +5905,7 @@ Request Body:
-
+
@@ -5849,13 +5942,13 @@ Request Body:
}
-
+
Save Collection Sheet
-
This Api allows the loan officer to perform bulk repayments of individual loans and deposit of mandatory savings on a given meeting date.
+
This Api allows the loan officer to perform bulk repayments of individual loans and deposit of mandatory savings on a given meeting date.
POST https://Domain Name/api/v1/collectionsheet?command=saveCollectionSheet
@@ -5891,7 +5984,7 @@ Request Body:
-
+
@@ -8940,7 +9033,7 @@ Request Body:
| expectedDisbursementDate |
-
+
@@ -9813,8 +9906,8 @@ Request Body:
-
-
+
+
@@ -9824,7 +9917,7 @@ Request Body:
POST https://DomainName/api/v1/loans/{loanId}/transactions?command=prepayLoan or
-
+
https://DomainName/api/v1/loans/{loanId}/transactions?command=prepayLoan&dateFormat=dd+MMMM+yyyy&locale=en&transactionDate=02+April+2015
@@ -9845,7 +9938,7 @@ Request Body:
-
+
@@ -11844,7 +11937,7 @@ Request Body:
canDefineInstallmentAmount |
- | if provided as true, then fixed instalment amount can be provided from loan account.
+ | if provided as true, then fixed instalment amount can be provided from loan account.
|
@@ -11977,7 +12070,7 @@ An enumeration that indicates the type of transaction processing strategy to be
Specifies compounding frequency start date for interest recalculation.
|
-
+
| recalculationRestFrequencyType |
@@ -13328,7 +13421,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
isArrearsBasedOnOriginalSchedule, preClosureInterestCalculationStrategy |
-
+
Additional Optional Fields if interest recalculation is enabled(true) and recalculationRestFrequencyType is not same as repayment period |
@@ -13336,7 +13429,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
recalculationRestFrequencyInterval, recalculationRestFrequencyDate |
-
+
Additional Optional Fields if interest recalculation is enabled(true) and interestRecalculationCompoundingMethod is enabled |
@@ -13344,9 +13437,9 @@ GET https://DomainName/api/v1/loanproducts/{productId}
recalculationCompoundingFrequencyType |
-
+
- Additional Optional Fields if interest recalculation is enabled(true) and interestRecalculationCompoundingMethod is enabled and
+ Additional Optional Fields if interest recalculation is enabled(true) and interestRecalculationCompoundingMethod is enabled and
recalculationCompoundingFrequencyType is not same as repayment period |
|
@@ -13408,7 +13501,7 @@ Request Body:
}
],
"allowAttributeOverrides":
- {amortizationType : true,
+ {amortizationType : true,
interestType : true,
transactionProcessingStrategyId : false,
interestCalculationPeriodType : true,
@@ -13416,7 +13509,7 @@ Request Body:
repaymentEvery : true,
graceOnPrincipalAndInterestPayment : true,
graceOnArrearsAgeing : true},
-
+
"accountingRule":"2",
"name":"product 5",
"shortName":"prd5",
@@ -14278,7 +14371,7 @@ Request Body:
| locale must be provided. |
-
+
@@ -16779,7 +16872,7 @@ POST scheduler?command=stop
-
+
@@ -16797,11 +16890,11 @@ POST scheduler?command=stop
host -
port -
useTLS -
-
+
-
+
@@ -16816,7 +16909,7 @@ POST scheduler?command=stop
GET https://DomainName/api/v1/externalservice/{serviceName}
-
+
[
{
@@ -16845,7 +16938,7 @@ GET https://DomainName/api/v1/externalservice/{serviceName}
-
+
@@ -16863,7 +16956,7 @@ Content-Type: application/json
{
"username" : "test@mifos.org"
"password" : "XXXX"
-}
+}
@@ -17204,8 +17297,8 @@ Request Body:
-
-
+
+
@@ -17347,7 +17440,7 @@ Request Body:
incomeAccount |
- | This field is Optional and can be used only when a charge is applied to a Client. It indicates the Income or Liability account which gets
+ | This field is Optional and can be used only when a charge is applied to a Client. It indicates the Income or Liability account which gets
credited when a payment is made on this charge
|
@@ -29448,7 +29541,7 @@ Request Body:
class="old-syle-anchor">
-
Retreive surveys
+
Retrieve surveys
Retrieve surveys. This allows to retrieve the list of survey tables registered .
@@ -29585,7 +29678,7 @@ Request Body:
class="old-syle-anchor">
-
Retreive survey
+
Retrieve survey
Lists a registered survey table details and the Mifos X Core application table they are registered to.
@@ -30897,7 +30990,7 @@ No Request Body
| Mandatory field.
- Indicates the client application identity.
+ Indicates the client application identity.
|
@@ -30905,7 +30998,7 @@ No Request Body
| Optional field.
- Indicates the client application password.
+ Indicates the client application password.
|
@@ -30913,7 +31006,7 @@ No Request Body
| Mandatory field.
- Application User(resource) login name.
+ Application User(resource) login name.
|
@@ -30921,7 +31014,7 @@ No Request Body
| Mandatory field.
- Application User(resource) password.
+ Application User(resource) password.
|
@@ -30953,8 +31046,8 @@ No Request Body
-
-
+
+
@@ -30978,7 +31071,7 @@ No Request Body
| Mandatory field.
- Indicates the client application identity.
+ Indicates the client application identity.
|
@@ -30986,7 +31079,7 @@ No Request Body
| Optional field.
- Indicates the client application password.
+ Indicates the client application password.
|
@@ -30994,7 +31087,7 @@ No Request Body
| Mandatory field.
- Application refresh token to generate access token.
+ Application refresh token to generate access token.
|
@@ -31113,7 +31206,7 @@ No Request Body
-
+
@@ -31440,8 +31533,8 @@ GET https://DomainName/api/v1/roles
-
-
+
+
@@ -31460,7 +31553,7 @@ No Request Body
{
"resourceId":1
-}
+}
@@ -31585,7 +31678,7 @@ GET https://DomainName/api/v1/roles/{roleId}/permissions
-
+
@@ -31598,17 +31691,17 @@ POST https://DomainName/api/v1/roles/{roleId}?command=enable
POST roles/1?command=enable
-Content-Type: application/json
+Content-Type: application/json
No Request Body
{
"resourceId":1
-}
+}
-
+
@@ -31621,7 +31714,7 @@ POST https://DomainName/api/v1/roles/{roleId}?command=disable
POST roles/1?command=disable
-Content-Type: application/json
+Content-Type: application/json
No Request Body
@@ -31797,7 +31890,7 @@ GET https://DomainName/api/v1/passwordpreferences/template
]
-
+
GET https://DomainName/api/v1/passwordpreferences/
@@ -33703,11 +33796,11 @@ Request Body:
Boolean variable whether to add journal entries for generated provisioning entries
-
+
-
+
@@ -33750,7 +33843,7 @@ Request Body:
"locale":"en",
"dateFormat":"dd MMMM yyyy",
"createjournalentries":true
- }
+ }
{
@@ -33759,14 +33852,14 @@ Request Body:
-
+
List all Provisioning Entries
List all Provisioning Entries
-
+
@@ -33777,8 +33870,8 @@ Request Body:
Content-Type: application/json
Request Body:
{
-
- }
+
+ }
{[
@@ -33789,14 +33882,14 @@ Request Body:
-
+
Retrieves a Provisioning Entry
Returns the details of a generated Provisioning Entry.
-
+
@@ -33807,14 +33900,14 @@ Request Body:
Content-Type: application/json
Request Body:
{
-
- }
+
+ }
{
- "id":1,
- "journalEntry":true,
- "createdUser":"mifos",
+ "id":1,
+ "journalEntry":true,
+ "createdUser":"mifos",
"createdDate":"16 October 2014",
"provisioningEntries":[
{"historyId":1,
@@ -33831,9 +33924,9 @@ Request Body:
"expenseAccount":6,
"expenseCode":"Expense Account Name"
},
- {"id":2,
- "journalEntry":true,
- "createdUser":"mifos",
+ {"id":2,
+ "journalEntry":true,
+ "createdUser":"mifos",
"createdDate":"16 October 2014",
"provisioningEntries":[
{"historyId":1,
@@ -33851,20 +33944,20 @@ Request Body:
"expenseCode":"Expense Account Name"
}
]
-
+
}
-
-
+
+
Recreates Provisioning Entry
Recreates Provisioning Entry.
-
+
@@ -33876,7 +33969,7 @@ Request Body:
Request Body:
{
"command":"recreateprovisioningentry"
- }
+ }
{
@@ -33885,14 +33978,14 @@ Request Body:
-
+
Retrieves a Provisioning Entry
Returns the details of a generated Provisioning Entry.
-
+
@@ -33904,9 +33997,9 @@ Request Body:
Request Body:
{
"command":"createjournalentry"
- }
+ }
-
+
{
"resourceId": 1
@@ -33914,8 +34007,8 @@ Request Body:
-
-
+
+
@@ -34053,7 +34146,7 @@ GET https://DomainName/api/v1/search?query=000000111
}
]
-
+
GET https://DomainName/api/v1/search?query=000000001&exactMatch=true
@@ -36002,11 +36095,11 @@ No Request Body:
Define minimum, maximum, percentage, liability account, expense account for all provisioning categories
-
+
-
+
@@ -36045,21 +36138,21 @@ No Request Body:
"criteriaName":"High Risk Products Criteria",
"loanProducts": [
{"id": 1,
- "name": "LOAN_PRODUCT_3ODPK1",
+ "name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
- "name": "LOAN_PRODUCT_BXW8NC",
+ "name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
- {"categoryId": 1,
- "categoryName": "STANDARD",
- "minAge": 0,
- "maxAge": 3,
+ {"categoryId": 1,
+ "categoryName": "STANDARD",
+ "minAge": 0,
+ "maxAge": 3,
"provisioningPercentage": 1,
- "liabilityAccount": 8,
+ "liabilityAccount": 8,
"expenseAccount": 14},
- {"categoryId": 2,
+ {"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
@@ -36078,9 +36171,9 @@ No Request Body:
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
- "liabilityAccount": 10,
+ "liabilityAccount": 10,
"expenseAccount": 9}]
- }
+ }
{
@@ -36104,8 +36197,8 @@ No Request Body:
GET provisioningcriteria
Content-Type: application/json
Request Body:
- {
- }
+ {
+ }
{
@@ -36116,7 +36209,7 @@ No Request Body:
{"criteriaId":2,
"criteriaName":"Low Risk Products Criteria",
"createdBy":"mifos"}
- ]
+ ]
}
@@ -36136,8 +36229,8 @@ No Request Body:
GET provisioningcriteria/{criteriaId}
Content-Type: application/json
Request Body:
- {
- }
+ {
+ }
{
@@ -36145,21 +36238,21 @@ No Request Body:
"criteriaName":"High Risk Products Criteria",
"loanProducts": [
{"id": 1,
- "name": "LOAN_PRODUCT_3ODPK1",
+ "name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
- "name": "LOAN_PRODUCT_BXW8NC",
+ "name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
- {"categoryId": 1,
- "categoryName": "STANDARD",
- "minAge": 0,
- "maxAge": 3,
+ {"categoryId": 1,
+ "categoryName": "STANDARD",
+ "minAge": 0,
+ "maxAge": 3,
"provisioningPercentage": 1,
- "liabilityAccount": 8,
+ "liabilityAccount": 8,
"expenseAccount": 14},
- {"categoryId": 2,
+ {"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
@@ -36178,7 +36271,7 @@ No Request Body:
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
- "liabilityAccount": 10,
+ "liabilityAccount": 10,
"expenseAccount": 9}]
}
@@ -36211,21 +36304,21 @@ No Request Body:
"criteriaName":"High Risk Products Criteria1",
"loanProducts": [
{"id": 1,
- "name": "LOAN_PRODUCT_3ODPK1",
+ "name": "LOAN_PRODUCT_3ODPK1",
"includeInBorrowerCycle": false},
{"id": 2,
- "name": "LOAN_PRODUCT_BXW8NC",
+ "name": "LOAN_PRODUCT_BXW8NC",
includeInBorrowerCycle": false}
],
"provisioningcriteria": [
- {"categoryId": 1,
- "categoryName": "STANDARD",
- "minAge": 0,
- "maxAge": 3,
+ {"categoryId": 1,
+ "categoryName": "STANDARD",
+ "minAge": 0,
+ "maxAge": 3,
"provisioningPercentage": 1,
- "liabilityAccount": 8,
+ "liabilityAccount": 8,
"expenseAccount": 14},
- {"categoryId": 2,
+ {"categoryId": 2,
"categoryName": "SUB-STANDARD",
"minAge": 1,
"maxAge": 5,
@@ -36244,9 +36337,9 @@ No Request Body:
"minAge": 3,
"maxAge": 7,
"provisioningPercentage": 4,
- "liabilityAccount": 10,
+ "liabilityAccount": 10,
"expenseAccount": 9}]
- }
+ }
{
@@ -36274,7 +36367,7 @@ No Request Body:
Content-Type: application/json
Request Body:
{
- }
+ }
{
@@ -36282,7 +36375,7 @@ No Request Body:
}
-
+
@@ -36324,7 +36417,7 @@ No Request Body:
Array of ratePeriod JSON objects as defined in below section.
|
-
+
This defines the Floating Rates Periods
@@ -36359,7 +36452,7 @@ No Request Body:
-
+
@@ -36415,7 +36508,7 @@ No Request Body:
"dateFormat":"dd MMMM yyyy"
}
]
- }
+ }
{
@@ -36526,7 +36619,7 @@ No Request Body:
Update Floating Rate
-
Updates new Floating Rate. Rate Periods in the past cannot be modified.
+
Updates new Floating Rate. Rate Periods in the past cannot be modified.
All the future rateperiods would be replaced with the new ratePeriods data sent.
@@ -36555,7 +36648,7 @@ No Request Body:
"dateFormat":"dd MMMM yyyy"
}
]
- }
+ }
{
@@ -36890,8 +36983,8 @@ Request Body:
-
-
+
+
@@ -36937,11 +37030,11 @@ Request Body:
| it specifies the due date |
-
+
-
+
@@ -36992,7 +37085,7 @@ Request Body:
| Effective date of the transaction |
-
+
@@ -37015,9 +37108,9 @@ Request Body:
Restricts the size of results returned. To override the default and return all entries you must explicitly pass a non-positive integer value for limit e.g. limit=0, or limit=-1
- Example Requests:
+ Example Requests:
clients/189/transactions
- clients/189/transactions?offset=10&limit=50
+ clients/189/transactions?offset=10&limit=50
@@ -37171,7 +37264,7 @@ Request Body:
List Client Charges
-
+
The list capability of client charges supports pagination.
Optional Arguments
@@ -37193,9 +37286,9 @@ Request Body:
- Filters charges that are pending payment (neither paid or waived).
-
Example Requests:
-
clients/1/charges
-
clients/1/charges?offset=0&limit=5
+
Example Requests:
+
clients/1/charges
+
clients/1/charges?offset=0&limit=5
@@ -37206,7 +37299,7 @@ GET clients/1/charges?limit=5&offset=0
Content-Type: application/json
No Request Body
-
+
{
"totalFilteredRecords": 4,
"pageItems": [
@@ -37363,8 +37456,8 @@ No Request Body
"isWaived": true
}
]
-}
-
+}
+
@@ -37442,8 +37535,8 @@ GET https://DomainName/api/v1/clients/{clientId}/charges/{clientChargeId}
}
-
-
+
+
@@ -37452,7 +37545,7 @@ GET https://DomainName/api/v1/clients/{clientId}/charges/{clientChargeId}
Deletes a Client Charge on which no transactions have taken place (either payments or waivers).
-
+
DELETE https://DomainName/api/v1/clients/{clientId}/charges/{clientChargeId}
@@ -37474,7 +37567,7 @@ No Request Body
Pay a Client Charge
-
Pay either a part of or the entire due amount for a charge.
+
Pay either a part of or the entire due amount for a charge.
@@ -37486,10 +37579,10 @@ No Request Body
-
+
-
+
POST https://DomainName/api/v1/clients/{clientId}/charges/{clientChargeId}?command=paycharge
@@ -37505,7 +37598,7 @@ Request Body:
}
-
+
{
"officeId":1,
"clientId":189,
@@ -37520,21 +37613,21 @@ Request Body:
Waive a Client Charge
-
This API provides the facility of waiving off the remaining amount on a client charge
+
This API provides the facility of waiving off the remaining amount on a client charge
-
+
-
+
POST https://DomainName/api/v1/clients/{clientId}/charges/{clientChargeId}?command=waive
-POST clients/189/charges/157?command=waive
+POST clients/189/charges/157?command=waive
Content-Type: application/json
No Request Body
-
+
{
"clientId":"189",
"resourceId":157
@@ -37542,23 +37635,23 @@ No Request Body
-
+
Undo a Client Transaction
-
+
POST https://DomainName/api/v1/clients/{clientId}/transactions/{transactionId}?command=undo
-POST clients/189/transactions/222?command=undo
+POST clients/189/transactions/222?command=undo
Content-Type: application/json
No Request Body
-
+
{
"officeId":1,
"clientId":189,
@@ -37566,8 +37659,8 @@ No Request Body
}
-
-
+
+
@@ -37592,14 +37685,394 @@ No Request Body:
+
+
+
+
+
SPM API
+
+ The Mifos X SPM API provides the ability to create custom surveys to collect social performance measurentment data or any additional questionnaire a financial institute want to collect.
+
+
+
+
+
+
+
Create a Survey
+
Adds a new survey to collect client related data.
+
+
+ Mandatory Fields |
+
+
+ | countryCode, key, name, questions, responses, sequenceNo, text, value |
+
+
+
+
+
+POST https://DomainName/api/v1/surveys
+
+
+POST surveys
+Content-Type: application/json
+Request Body:
+{
+ "key":"ppi-kenya-2010",
+ "name":"PPI Survey for Kenya, version 2010",
+ "description":null,
+ "countryCode":"KE",
+ "validFrom":null,
+ "validTo":null,
+ "components":
+ [
+ {
+ "key":"Household",
+ "text":"Information about the household."
+ "description":null,
+ "sequenceNo":1
+ }
+ ]
+ "questions":
+ [
+ {
+ "componentKey":"Household",
+ "key":"Familiy members",
+ "text":"How many persons live in the household?"
+ "description":null,
+ "sequenceNo":1,
+ "responses":
+ [
+ {
+ "text":"1 to 3",
+ "value":1,
+ "sequenceNo":1
+ },
+ {
+ "text":"3 to 7",
+ "value":5,
+ "sequenceNo":2
+ },
+ {
+ "text":"more than 7",
+ "value":13,
+ "sequenceNo":3
+ }
+ ]
+ }
+ ]
+}
+
+200 OK
+
+
+
+
+
+
+
List all Surveys
+
+
+
+GET https://DomainName/api/v1/surveys
+
+
+GET surveys
+Content-Type: application/json
+No Request Body
+
+
+[
+ {
+ "id":1
+ "key":"ppi-kenya-2010",
+ "name":"PPI Survey for Kenya, version 2010",
+ "description":null,
+ "countryCode":"KE",
+ "validFrom":"2015-11-25T00:00:00.000Z",
+ "validTo":"2115-12-31T23:59:59.999Z",
+ "components":
+ [
+ {
+ "key":"Household",
+ "text":"Information about the household."
+ "description":null,
+ "sequenceNo":1
+ }
+ ]
+ "questions":
+ [
+ {
+ "componentKey":"Household",
+ "key":"Familiy members",
+ "text":"How many persons live in the household?"
+ "description":null,
+ "sequenceNo":1,
+ "responses":
+ [
+ {
+ "text":"1 to 3",
+ "value":1,
+ "sequenceNo":1
+ },
+ {
+ "text":"3 to 7",
+ "value":5,
+ "sequenceNo":2
+ },
+ {
+ "text":"more than 7",
+ "value":13,
+ "sequenceNo":3
+ }
+ ]
+ }
+ ]
+ }
+]
+
+
+
+
+
+
+
Retrieve a Survey
+
+
+
+GET https://DomainName/api/v1/surveys/1
+
+
+GET surveys/1
+Content-Type: application/json
+No Request Body
+
+
+{
+ "id":1
+ "key":"ppi-kenya-2010",
+ "name":"PPI Survey for Kenya, version 2010",
+ "description":null,
+ "countryCode":"KE",
+ "validFrom":"2015-11-25T00:00:00.000Z",
+ "validTo":"2115-12-31T23:59:59.999Z",
+ "components":
+ [
+ {
+ "key":"Household",
+ "text":"Information about the household."
+ "description":null,
+ "sequenceNo":1
+ }
+ ]
+ "questions":
+ [
+ {
+ "componentKey":"Household",
+ "key":"Familiy members",
+ "text":"How many persons live in the household?"
+ "description":null,
+ "sequenceNo":1,
+ "responses":
+ [
+ {
+ "text":"1 to 3",
+ "value":1,
+ "sequenceNo":1
+ },
+ {
+ "text":"3 to 7",
+ "value":5,
+ "sequenceNo":2
+ },
+ {
+ "text":"more than 7",
+ "value":13,
+ "sequenceNo":3
+ }
+ ]
+ }
+ ]
+}
+
+
+
+
+
+
+
Deactivate Survey
+
+
+
+DELETE https://DomainName/api/v1/surveys/1
+
+
+DELETE surveys/1
+Content-Type: application/json
+No Request Body
+
+
+200 OK
+
+
+
+
+
+
+
Create a Lookup Table entry
+
Add a new netry to a survey.
+
+
+ Mandatory Fields |
+
+
+ | key, score, validFrom, validTo |
+
+
+
+
+
+POST https://DomainName/api/v1/surveys/1/lookuptables
+
+
+POST surveys/1/lookuptables
+Content-Type: application/json
+Request Body:
+{
+ "key":"lookup-table-1",
+ "description":null,
+ "valueFrom":0,
+ "valueTo":5,
+ "score":100.00
+}
+
+
+200 OK
+
+
+
+
+
+
+
List all Lookup Table entries
+
List all Lookup Table entries for a survey.
+
+
+
+GET https://DomainName/api/v1/surveys/1/lookuptables
+
+
+GET surveys/1/lookuptables
+Content-Type: application/json
+No Request Body
+
+
+[
+ {
+ "id":1
+ "key":"lookup-table-1",
+ "description":null,
+ "valueFrom":0,
+ "valueTo":5,
+ "score":100.00
+ }
+]
+
+
+
+
+
+
+
Retrieve a Lookup Table entry
+
Retrieve a Lookup Table entry for a survey.
+
+
+
+GET https://DomainName/api/v1/surveys/1/lookuptables/1
+
+
+GET surveys/1/lookuptables/1
+Content-Type: application/json
+No Request Body
+
+
+{
+ "id":1,
+ "key":"lookup-table-1",
+ "description":null,
+ "valueFrom":0,
+ "valueTo":5,
+ "score":100.00
+}
+
+
+
-
-
-
-
-
-
+
+
+
+
Create a Scorecard entry
+
Add a new netry to a survey.
+
+
+ Mandatory Fields |
+
+
+ | clientId, createdOn, questionId, responseId, staffId |
+
+
+
+
+
+POST https://DomainName/api/v1/surveys/1/scorecards
+
+
+POST surveys/1/scorecards
+Content-Type: application/json
+Request Body:
+{
+ "questionId":1,
+ "responseId":1,
+ "staffId":1,
+ "clientId":1,
+ "createdOn":"2015-11-26T09:50:34.647Z"
+}
+
+
+200 OK
+
+
+
+
+
+
+
List all Scorecard entries
+
List all Scorecard entries for a survey.
+
+
+
+GET https://DomainName/api/v1/surveys/1/scorecards
+
+
+GET surveys/1/scorecards
+Content-Type: application/json
+No Request Body
+
+
+[
+ {
+ "questionId":1,
+ "responseId":1,
+ "staffId":1,
+ "clientId":1,
+ "createdOn":"2015-11-26T09:50:34.647Z"
+ }
+]
+
+
+
+
diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties
index 5bac1bb76..819de4df2 100644
--- a/mifosng-provider/gradle.properties
+++ b/mifosng-provider/gradle.properties
@@ -1,3 +1,3 @@
-releaseVersion=15.11.2.RELEASE
+releaseVersion=15.12.1.RELEASE
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java
index 30c984df4..c235d56e9 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/provisioning/service/ProvisioningEntriesReadPlatformServiceImpl.java
@@ -68,8 +68,8 @@ public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningE
.append("GREATEST(datediff(").append(formattedDate).append(",sch.duedate),0) <= pcd.max_age) and ")
.append("pcd.criteria_id is not null ").append("LEFT JOIN m_client mclient ON mclient.id = loan.client_id ")
.append("LEFT JOIN m_group mgroup ON mgroup.id = loan.group_id ")
- .append("where loan.loan_status_id=300 and sch.completed_derived=false ")
- .append("GROUP BY loan.id order by loan.product_id");
+ .append("where loan.loan_status_id=300 and sch.duedate = ")
+ .append("(select MIN(sch1.duedate) from m_loan_repayment_schedule sch1 where sch1.loan_id=loan.id and sch1.completed_derived=false)");
}
@Override
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java
index 8149a6b4f..35ea2f751 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java
@@ -49,4 +49,5 @@ public interface ConfigurationDomainService {
int getRoundingMode();
+ boolean isBackdatePenaltiesEnabled();
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index aece63d72..1d2ebc154 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -187,10 +187,18 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
if (property.isEnabled()) {
int value = property.getValue().intValue();
- if (value < 0 || value > 6) { return defaultValue; }
+ if (value < 0 || value > 6) {
+ return defaultValue;
+ }
return value;
}
return defaultValue;
}
+ public boolean isBackdatePenaltiesEnabled() {
+ final String propertyName = "backdate-penalties-enabled";
+ final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
+ return property.isEnabled();
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index 52cd96b74..363bc1d77 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -45,6 +45,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbu
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
+import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
/**
@@ -124,6 +125,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
// actual outstanding balance for interest calculation
Money outstandingBalance = principalToBeScheduled;
+ //Set Fixed Principal Amount
+ if(loanApplicationTerms.getAmortizationMethod().equals(AmortizationMethod.EQUAL_PRINCIPAL)) {
+ loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber, outstandingBalance) ;
+ }
+
// Set fixed EMI Amount
if (loanApplicationTerms.getFixedEmiAmount() == null) {
updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO);
@@ -221,6 +227,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
loanApplicationTerms.updateAnnualNominalInterestRate(variation.getTermValue());
}
}
+ isFirstRepayment = false;
}
while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java
index e1f49a61a..99fca09d2 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -76,7 +76,7 @@ public interface LoanReadPlatformService {
* result overdue installments with this charge already applied are not
* returned.
*/
- Collection retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod);
+ Collection retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod, final Boolean backdatePenalties);
Integer retriveLoanCounter(Long groupId, Integer loanType, Long productId);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index e1d22134e..70a4396f7 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -17,8 +17,11 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
@@ -1465,12 +1468,31 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
@Override
- public Collection retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod) {
+ public Collection retrieveAllLoansWithOverdueInstallments(final Long penaltyWaitPeriod, final Boolean backdatePenalties) {
final MusoniOverdueLoanScheduleMapper rm = new MusoniOverdueLoanScheduleMapper();
- final String sql = "select " + rm.schema() + " where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate "
- + " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 "
- + " and mc.charge_time_enum = 9 and ml.loan_status_id = 300 ";
- return this.jdbcTemplate.query(sql, rm, new Object[] { penaltyWaitPeriod });
+
+ final StringBuilder sqlBuilder = new StringBuilder(400);
+ sqlBuilder
+ .append("select ")
+ .append(rm.schema())
+ .append(" where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate ")
+ .append( " and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 ")
+ .append(" and mc.charge_time_enum = 9 and ml.loan_status_id = 300 ");
+
+
+ if(backdatePenalties)
+ {
+ return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod });
+ }
+ else
+ {
+ // Only apply for duedate = yesterday (so that we don't apply penalties on the duedate itself)
+ sqlBuilder.append(" and ls.duedate >= DATE_SUB(CURDATE(),INTERVAL (? + 1) DAY)");
+
+ return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod, penaltyWaitPeriod });
+ }
+
+
}
@SuppressWarnings("deprecation")
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanSchedularServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanSchedularServiceImpl.java
index 914fe998a..249970f1c 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanSchedularServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanSchedularServiceImpl.java
@@ -49,8 +49,9 @@ public class LoanSchedularServiceImpl implements LoanSchedularService {
public void applyChargeForOverdueLoans() throws JobExecutionException {
final Long penaltyWaitPeriodValue = this.configurationDomainService.retrievePenaltyWaitPeriod();
+ final Boolean backdatePenalties = this.configurationDomainService.isBackdatePenaltiesEnabled();
final Collection overdueLoanScheduledInstallments = this.loanReadPlatformService
- .retrieveAllLoansWithOverdueInstallments(penaltyWaitPeriodValue);
+ .retrieveAllLoansWithOverdueInstallments(penaltyWaitPeriodValue,backdatePenalties);
if (!overdueLoanScheduledInstallments.isEmpty()) {
final StringBuilder sb = new StringBuilder();
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 3f8d0d140..c95b223c9 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -2441,7 +2441,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
- @Override
@Transactional
public void applyOverdueChargesForLoan(final Long loanId, Collection overdueLoanScheduleDatas) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java
index 99db4bd5a..3cf65eef7 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransaction.java
@@ -5,8 +5,12 @@
*/
package org.mifosplatform.portfolio.savings.domain;
-import java.math.BigDecimal;
-import java.util.Date;
+import org.joda.time.LocalDate;
+import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
+import org.mifosplatform.organisation.monetary.domain.Money;
+import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransaction;
+import org.mifosplatform.portfolio.savings.DepositAccountOnHoldTransactionType;
+import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -17,11 +21,8 @@ import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
-
-import org.joda.time.LocalDate;
-import org.mifosplatform.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransaction;
-import org.mifosplatform.portfolio.savings.DepositAccountOnHoldTransactionType;
-import org.springframework.data.jpa.domain.AbstractPersistable;
+import java.math.BigDecimal;
+import java.util.Date;
@Entity
@Table(name = "m_deposit_account_on_hold_transaction")
@@ -81,6 +82,10 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistable {
return this.amount;
}
+ public Money getAmountMoney(final MonetaryCurrency currency) {
+ return Money.of(currency, this.amount);
+ }
+
public void reverseTransaction() {
this.reversed = true;
if (this.getTransactionType().isHold()) {
@@ -92,7 +97,14 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistable {
public DepositAccountOnHoldTransactionType getTransactionType() {
return DepositAccountOnHoldTransactionType.fromInt(this.transactionType);
+ }
+ public LocalDate getTransactionDate() {
+ LocalDate transactionDate = null;
+ if(this.transactionDate !=null){
+ transactionDate = LocalDate.fromDateFields(this.transactionDate);
+ }
+ return transactionDate;
}
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java
index 3b0259354..e476824df 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/DepositAccountOnHoldTransactionRepository.java
@@ -8,7 +8,10 @@ package org.mifosplatform.portfolio.savings.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import java.util.List;
+
public interface DepositAccountOnHoldTransactionRepository extends JpaRepository,
JpaSpecificationExecutor {
+ List findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(SavingsAccount account);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
old mode 100644
new mode 100755
index fabb39e56..0fa7a0ab1
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java
@@ -5,52 +5,7 @@
*/
package org.mifosplatform.portfolio.savings.domain;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.DiscriminatorColumn;
-import javax.persistence.DiscriminatorType;
-import javax.persistence.DiscriminatorValue;
-import javax.persistence.Embedded;
-import javax.persistence.Entity;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
-import javax.persistence.OneToMany;
-import javax.persistence.OrderBy;
-import javax.persistence.Table;
-import javax.persistence.Temporal;
-import javax.persistence.TemporalType;
-import javax.persistence.Transient;
-import javax.persistence.UniqueConstraint;
-import javax.persistence.Version;
-
+import com.google.gson.JsonArray;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.LazyCollection;
@@ -96,7 +51,50 @@ import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.util.CollectionUtils;
-import com.google.gson.JsonArray;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorType;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Embedded;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OrderBy;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.persistence.Transient;
+import javax.persistence.UniqueConstraint;
+import javax.persistence.Version;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
@Entity
@Table(name = "m_savings_account", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "sa_account_no_UNIQUE"),
@@ -859,10 +857,11 @@ public class SavingsAccount extends AbstractPersistable {
return transactionBeforeLastInterestPosting;
}
- public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isException) {
+ public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount, final boolean isException,final List depositAccountOnHoldTransactions) {
final List transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency);
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
+ LocalDate lastSavingsDate = null;
for (final SavingsAccountTransaction transaction : transactionsSortedByDate) {
if (transaction.isNotReversed() && transaction.isCredit()) {
runningBalance = runningBalance.plus(transaction.getAmount(this.currency));
@@ -872,22 +871,44 @@ public class SavingsAccount extends AbstractPersistable {
continue;
}
+
+
final BigDecimal withdrawalFee = null;
+
+ /* Loop through the onHold funds and see if we need to deduct or add to minimum required balance
+ and the point in time the transaction was made: */
+ if(depositAccountOnHoldTransactions !=null){
+ for(final DepositAccountOnHoldTransaction onHoldTransaction: depositAccountOnHoldTransactions) {
+ // Compare the balance of the on hold:
+ if((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction.getTransactionDate().isEqual(transaction.transactionLocalDate()))
+ && (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) {
+ if(onHoldTransaction.getTransactionType().isHold()) {
+ minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency));
+ }else{
+ minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency));
+ }
+ }
+ }
+ }
+
+
// deal with potential minRequiredBalance and
// enforceMinRequiredBalance
if (!isException && transaction.canProcessBalanceCheck()) {
if (runningBalance.minus(minRequiredBalance).isLessThanZero()) { throw new InsufficientAccountBalanceException(
"transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount); }
}
+ lastSavingsDate = transaction.transactionLocalDate();
}
}
- public void validateAccountBalanceDoesNotBecomeNegative(final String transactionAction) {
+ public void validateAccountBalanceDoesNotBecomeNegative(final String transactionAction,final List depositAccountOnHoldTransactions) {
final List transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency);
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
+ LocalDate lastSavingsDate = null;
for (final SavingsAccountTransaction transaction : transactionsSortedByDate) {
if (transaction.isNotReversed() && transaction.isCredit()) {
runningBalance = runningBalance.plus(transaction.getAmount(this.currency));
@@ -895,6 +916,22 @@ public class SavingsAccount extends AbstractPersistable {
runningBalance = runningBalance.minus(transaction.getAmount(this.currency));
}
+ /* Loop through the onHold funds and see if we need to deduct or add to minimum required balance
+ and the point in time the transaction was made: */
+ if(depositAccountOnHoldTransactions !=null){
+ for(final DepositAccountOnHoldTransaction onHoldTransaction: depositAccountOnHoldTransactions) {
+ // Compare the balance of the on hold:
+ if((onHoldTransaction.getTransactionDate().isBefore(transaction.transactionLocalDate()) || onHoldTransaction.getTransactionDate().isEqual(transaction.transactionLocalDate()))
+ && (lastSavingsDate == null || onHoldTransaction.getTransactionDate().isAfter(lastSavingsDate))) {
+ if(onHoldTransaction.getTransactionType().isHold()) {
+ minRequiredBalance = minRequiredBalance.plus(onHoldTransaction.getAmountMoney(this.currency));
+ }else{
+ minRequiredBalance = minRequiredBalance.minus(onHoldTransaction.getAmountMoney(this.currency));
+ }
+ }
+ }
+ }
+
// enforceMinRequiredBalance
if (transaction.canProcessBalanceCheck()) {
if (runningBalance.minus(minRequiredBalance).isLessThanZero()) {
@@ -910,6 +947,8 @@ public class SavingsAccount extends AbstractPersistable {
}
}
+ lastSavingsDate = transaction.transactionLocalDate();
+
}
}
@@ -2454,7 +2493,7 @@ public class SavingsAccount extends AbstractPersistable {
}
private Money minRequiredBalanceDerived(final MonetaryCurrency currency) {
- Money minReqBalance = Money.of(currency, this.onHoldFunds);
+ Money minReqBalance = Money.zero(currency);
if (this.enforceMinRequiredBalance) {
minReqBalance = minReqBalance.plus(this.minRequiredBalance);
}
@@ -2477,6 +2516,6 @@ public class SavingsAccount extends AbstractPersistable {
}
public BigDecimal getWithdrawableBalance() {
- return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount());
+ return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds());
}
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java
old mode 100644
new mode 100755
index b4753400e..fa36b4ff3
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java
@@ -5,13 +5,6 @@
*/
package org.mifosplatform.portfolio.savings.domain;
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
@@ -30,6 +23,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
@Service
public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainService {
@@ -39,19 +40,22 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final ConfigurationDomainService configurationDomainService;
+ private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
@Autowired
public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService,
- final ConfigurationDomainService configurationDomainService, final PlatformSecurityContext context) {
+ final ConfigurationDomainService configurationDomainService, final PlatformSecurityContext context,
+ final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) {
this.savingsAccountRepository = savingsAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.configurationDomainService = configurationDomainService;
this.context = context;
+ this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
}
@Transactional
@@ -84,7 +88,11 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
}
- account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck());
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+ account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(), depositAccountOnHoldTransactions);
saveTransactionToGenerateTransactionId(withdrawal);
this.savingsAccountRepository.save(account);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java
old mode 100644
new mode 100755
index 770b50890..6d787f2f7
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -5,24 +5,6 @@
*/
package org.mifosplatform.portfolio.savings.service;
-import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
@@ -42,7 +24,6 @@ import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
import org.mifosplatform.infrastructure.jobs.service.JobName;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.holiday.domain.HolidayRepositoryWrapper;
-import org.mifosplatform.organisation.holiday.service.HolidayWritePlatformService;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -51,7 +32,6 @@ import org.mifosplatform.organisation.monetary.domain.MoneyHelper;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
-import org.mifosplatform.organisation.workingdays.service.WorkingDaysWritePlatformService;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferDTO;
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
@@ -86,6 +66,8 @@ import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeDataValidato
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO;
import org.mifosplatform.portfolio.savings.domain.DepositAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.DepositAccountDomainService;
+import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransaction;
+import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository;
import org.mifosplatform.portfolio.savings.domain.DepositAccountRecurringDetail;
import org.mifosplatform.portfolio.savings.domain.FixedDepositAccount;
import org.mifosplatform.portfolio.savings.domain.RecurringDepositAccount;
@@ -105,6 +87,23 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mifosplatform.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
+
@Service
public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements DepositAccountWritePlatformService {
@@ -129,6 +128,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
private final ConfigurationDomainService configurationDomainService;
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
+ private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
+
@Autowired
public DepositAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -147,7 +148,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final DepositAccountReadPlatformService depositAccountReadPlatformService,
- final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService) {
+ final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService,
+ final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
@@ -170,6 +172,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
this.depositAccountReadPlatformService = depositAccountReadPlatformService;
this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
+ this.depositAccountOnHoldTransactionRepository =depositAccountOnHoldTransactionRepository;
}
@Transactional
@@ -235,7 +238,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
this.savingAccountRepository.save(account);
}
@@ -327,8 +334,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
this.savingAccountRepository.save(account);
}
@@ -586,7 +597,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions);
// account.activateAccountBasedOnBalance();
final boolean isPreMatureClosure = false;
account.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -682,8 +698,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions);
account.activateAccountBasedOnBalance();
if (savingsAccountTransaction.isDeposit()) {
@@ -1118,8 +1138,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions);
this.savingAccountRepository.saveAndFlush(account);
@@ -1245,8 +1269,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
+ account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions);
this.savingAccountRepository.save(account);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
old mode 100644
new mode 100755
index 6a2faae29..ad8520938
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -5,24 +5,6 @@
*/
package org.mifosplatform.portfolio.savings.service;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
-import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
@@ -68,6 +50,8 @@ import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeDataValidato
import org.mifosplatform.portfolio.savings.data.SavingsAccountDataValidator;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator;
+import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransaction;
+import org.mifosplatform.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountCharge;
@@ -89,6 +73,23 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_CHARGE_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
+import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
+
@Service
public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService {
@@ -113,6 +114,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private final HolidayRepositoryWrapper holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final ConfigurationDomainService configurationDomainService;
+ private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
+
@Autowired
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -130,7 +133,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository,
- final StaffRepositoryWrapper staffRepository, final ConfigurationDomainService configurationDomainService) {
+ final StaffRepositoryWrapper staffRepository, final ConfigurationDomainService configurationDomainService,
+ final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
@@ -152,6 +156,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsRepository = savingsRepository;
this.staffRepository = staffRepository;
this.configurationDomainService = configurationDomainService;
+ this.depositAccountOnHoldTransactionRepository= depositAccountOnHoldTransactionRepository;
}
@Transactional
@@ -208,7 +213,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
}
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, user);
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name());
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
}
@Transactional
@@ -440,7 +449,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction);
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions);
account.activateAccountBasedOnBalance();
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -525,7 +538,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction);
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions);
account.activateAccountBasedOnBalance();
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
@@ -869,8 +886,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction);
+ account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions);
this.savingAccountRepository.saveAndFlush(account);
@@ -995,8 +1016,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
}
+ List depositAccountOnHoldTransactions = null;
+ if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
+ depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
+ }
- account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode());
+ account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions);
this.savingAccountRepository.save(account);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/api/LookupTableApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/LookupTableApiResource.java
new file mode 100644
index 000000000..e70ceb271
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/LookupTableApiResource.java
@@ -0,0 +1,104 @@
+/**
+ * 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.spm.api;
+
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.spm.data.LookupTableData;
+import org.mifosplatform.spm.domain.LookupTable;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.exception.LookupTableNotFoundException;
+import org.mifosplatform.spm.exception.SurveyNotFoundException;
+import org.mifosplatform.spm.service.LookupTableService;
+import org.mifosplatform.spm.service.SpmService;
+import org.mifosplatform.spm.util.LookupTableMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/surveys/{surveyId}/lookuptables")
+@Component
+@Scope("singleton")
+public class LookupTableApiResource {
+
+ private final PlatformSecurityContext securityContext;
+ private final SpmService spmService;
+ private final LookupTableService lookupTableService;
+
+ @Autowired
+ public LookupTableApiResource(final PlatformSecurityContext securityContext,
+ final SpmService spmService,
+ final LookupTableService lookupTableService) {
+ super();
+ this.securityContext = securityContext;
+ this.spmService = spmService;
+ this.lookupTableService = lookupTableService;
+ }
+
+ @GET
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public List fetchLookupTables(@PathParam("surveyId") final Long surveyId) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = findSurvey(surveyId);
+
+ final List result = new ArrayList<>();
+
+ final List lookupTables = this.lookupTableService.findBySurvey(survey);
+
+ if (lookupTables != null) {
+ for (final LookupTable lookupTable : lookupTables) {
+ result.add(LookupTableMapper.map(lookupTable));
+ }
+ }
+
+ return result;
+ }
+
+ @GET
+ @Path("/{id}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public LookupTableData findLookupTable(@PathParam("surveyId") final Long surveyId,
+ @PathParam("id") final Long id) {
+ this.securityContext.authenticatedUser();
+
+ findSurvey(surveyId);
+
+ final LookupTable lookupTable = this.lookupTableService.findById(id);
+
+ if (lookupTable == null) {
+ throw new LookupTableNotFoundException(id);
+ }
+
+ return LookupTableMapper.map(lookupTable);
+ }
+
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public void createLookupTable(@PathParam("surveyId") final Long surveyId,
+ final LookupTableData lookupTableData) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = findSurvey(surveyId);
+
+ this.lookupTableService.createLookupTable(LookupTableMapper.map(lookupTableData, survey));
+ }
+
+ private Survey findSurvey(final Long surveyId) {
+ final Survey survey = this.spmService.findById(surveyId);
+ if (survey == null) {
+ throw new SurveyNotFoundException(surveyId);
+ }
+ return survey;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/api/ScorecardApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/ScorecardApiResource.java
new file mode 100644
index 000000000..3bb7dd7f2
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/ScorecardApiResource.java
@@ -0,0 +1,105 @@
+/**
+ * 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.spm.api;
+
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.organisation.staff.domain.Staff;
+import org.mifosplatform.organisation.staff.domain.StaffRepository;
+import org.mifosplatform.organisation.staff.exception.StaffNotFoundException;
+import org.mifosplatform.portfolio.client.domain.Client;
+import org.mifosplatform.portfolio.client.domain.ClientRepository;
+import org.mifosplatform.portfolio.client.exception.ClientNotFoundException;
+import org.mifosplatform.spm.data.ScorecardData;
+import org.mifosplatform.spm.domain.Scorecard;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.exception.SurveyNotFoundException;
+import org.mifosplatform.spm.service.ScorecardService;
+import org.mifosplatform.spm.service.SpmService;
+import org.mifosplatform.spm.util.ScorecardMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/surveys/{surveyId}/scorecards")
+@Component
+@Scope("singleton")
+public class ScorecardApiResource {
+
+ private final PlatformSecurityContext securityContext;
+ private final SpmService spmService;
+ private final ScorecardService scorecardService;
+ private final StaffRepository staffRepository;
+ private final ClientRepository clientRepository;
+
+ @Autowired
+ public ScorecardApiResource(final PlatformSecurityContext securityContext, final SpmService spmService,
+ final ScorecardService scorecardService, final StaffRepository staffRepository,
+ final ClientRepository clientRepository) {
+ super();
+ this.securityContext = securityContext;
+ this.spmService = spmService;
+ this.scorecardService = scorecardService;
+ this.staffRepository = staffRepository;
+ this.clientRepository = clientRepository;
+ }
+
+ @GET
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public List findBySurvey(@PathParam("surveyId") final Long surveyId) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = findSurvey(surveyId);
+
+ final List result = new ArrayList<>();
+
+ final List scorecards = this.scorecardService.findBySurvey(survey);
+
+ if (scorecards == null) {
+ for (final Scorecard scorecard : scorecards) {
+ result.add(ScorecardMapper.map(scorecard));
+ }
+ }
+
+ return result;
+ }
+
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public void createScorecard(@PathParam("surveyId") final Long surveyId, final ScorecardData scorecardData) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = findSurvey(surveyId);
+
+ final Staff staff = this.staffRepository.findOne(scorecardData.getStaffId());
+
+ if (staff == null) {
+ throw new StaffNotFoundException(scorecardData.getStaffId());
+ }
+
+ final Client client = this.clientRepository.findOne(scorecardData.getClientId());
+
+ if (client == null) {
+ throw new ClientNotFoundException(scorecardData.getClientId());
+ }
+
+ this.scorecardService.createScorecard(ScorecardMapper.map(scorecardData, survey, staff, client));
+ }
+
+ private Survey findSurvey(final Long surveyId) {
+ final Survey survey = this.spmService.findById(surveyId);
+ if (survey == null) {
+ throw new SurveyNotFoundException(surveyId);
+ }
+ return survey;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/api/SpmApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/SpmApiResource.java
new file mode 100644
index 000000000..c994f6c3a
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/api/SpmApiResource.java
@@ -0,0 +1,93 @@
+/**
+ * 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.spm.api;
+
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.spm.data.SurveyData;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.exception.SurveyNotFoundException;
+import org.mifosplatform.spm.service.SpmService;
+import org.mifosplatform.spm.util.SurveyMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/surveys")
+@Component
+@Scope("singleton")
+public class SpmApiResource {
+
+ private final PlatformSecurityContext securityContext;
+ private final SpmService spmService;
+
+ @Autowired
+ public SpmApiResource(final PlatformSecurityContext securityContext,
+ final SpmService spmService) {
+ this.securityContext = securityContext;
+ this.spmService = spmService;
+ }
+
+ @GET
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public List fetchActiveSurveys() {
+ this.securityContext.authenticatedUser();
+
+ final List result = new ArrayList<>();
+
+ final List surveys = this.spmService.fetchValidSurveys();
+
+ if (surveys != null) {
+ for (final Survey survey : surveys) {
+ result.add(SurveyMapper.map(survey));
+ }
+ }
+
+ return result;
+ }
+
+ @GET
+ @Path("/{id}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public SurveyData findSurvey(@PathParam("id") final Long id) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = this.spmService.findById(id);
+
+ if (survey == null) {
+ throw new SurveyNotFoundException(id);
+ }
+
+ return SurveyMapper.map(survey);
+ }
+
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public void createSurvey(final SurveyData surveyData) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = SurveyMapper.map(surveyData);
+
+ this.spmService.createSurvey(survey);
+ }
+
+ @DELETE
+ @Path("/{id}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public void deactivateSurvey(@PathParam("id") final Long id) {
+ this.securityContext.authenticatedUser();
+
+ this.spmService.deactivateSurvey(id);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ComponentData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ComponentData.java
new file mode 100644
index 000000000..ee221e9e2
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ComponentData.java
@@ -0,0 +1,59 @@
+/**
+ * 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.spm.data;
+
+public class ComponentData {
+
+ private String key;
+ private String text;
+ private String description;
+ private Integer sequenceNo;
+
+ public ComponentData() {
+ super();
+ }
+
+ public ComponentData(final String key, final String text,
+ final String description, final Integer sequenceNo) {
+ super();
+ this.key = key;
+ this.text = text;
+ this.description = description;
+ this.sequenceNo = sequenceNo;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String title) {
+ this.text = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/LookupTableData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/LookupTableData.java
new file mode 100644
index 000000000..005f57610
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/LookupTableData.java
@@ -0,0 +1,79 @@
+/**
+ * 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.spm.data;
+
+public class LookupTableData {
+
+ private Long id;
+ private String key;
+ private String description;
+ private Integer valueFrom;
+ private Integer valueTo;
+ private Double score;
+
+ public LookupTableData() {
+ super();
+ }
+
+ public LookupTableData(final Long id, final String key, final String description, final Integer valueFrom,
+ final Integer valueTo, final Double score) {
+ super();
+ this.id = id;
+ this.key = key;
+ this.description = description;
+ this.valueFrom = valueFrom;
+ this.valueTo = valueTo;
+ this.score = score;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getValueFrom() {
+ return valueFrom;
+ }
+
+ public void setValueFrom(Integer valueFrom) {
+ this.valueFrom = valueFrom;
+ }
+
+ public Integer getValueTo() {
+ return valueTo;
+ }
+
+ public void setValueTo(Integer valueTo) {
+ this.valueTo = valueTo;
+ }
+
+ public Double getScore() {
+ return score;
+ }
+
+ public void setScore(Double score) {
+ this.score = score;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/QuestionData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/QuestionData.java
new file mode 100644
index 000000000..31f6d990f
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/QuestionData.java
@@ -0,0 +1,81 @@
+/**
+ * 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.spm.data;
+
+import java.util.List;
+
+public class QuestionData {
+
+ private List responseDatas;
+ private String componentKey;
+ private String key;
+ private String text;
+ private String description;
+ private Integer sequenceNo;
+
+ public QuestionData() {
+ super();
+ }
+
+ public QuestionData(final List responseDatas, final String componentKey, final String key,
+ final String text, final String description, final Integer sequenceNo) {
+ super();
+ this.responseDatas = responseDatas;
+ this.componentKey = componentKey;
+ this.key = key;
+ this.text = text;
+ this.description = description;
+ this.sequenceNo = sequenceNo;
+ }
+
+ public List getResponseDatas() {
+ return responseDatas;
+ }
+
+ public void setResponseDatas(List responseDatas) {
+ this.responseDatas = responseDatas;
+ }
+
+ public String getComponentKey() {
+ return componentKey;
+ }
+
+ public void setComponentKey(String componentKey) {
+ this.componentKey = componentKey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ResponseData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ResponseData.java
new file mode 100644
index 000000000..c96bafac8
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ResponseData.java
@@ -0,0 +1,49 @@
+/**
+ * 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.spm.data;
+
+public class ResponseData {
+
+ private String text;
+ private Integer value;
+ private Integer sequenceNo;
+
+ public ResponseData() {
+ super();
+ }
+
+ public ResponseData(final String text, final Integer value,
+ final Integer sequenceNo) {
+ super();
+ this.text = text;
+ this.value = value;
+ this.sequenceNo = sequenceNo;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public void setValue(Integer value) {
+ this.value = value;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ScorecardData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ScorecardData.java
new file mode 100644
index 000000000..fd1d511b3
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/ScorecardData.java
@@ -0,0 +1,81 @@
+/**
+ * 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.spm.data;
+
+import java.util.Date;
+
+public class ScorecardData {
+
+ private Long questionId;
+ private Long responseId;
+ private Long staffId;
+ private Long clientId;
+ private Date createdOn;
+ private Integer value;
+
+ public ScorecardData() {
+ super();
+ }
+
+ public ScorecardData(final Long questionId, final Long responseId, final Long staffId, final Long clientId,
+ final Date createdOn, final Integer value) {
+ super();
+ this.questionId = questionId;
+ this.responseId = responseId;
+ this.staffId = staffId;
+ this.clientId = clientId;
+ this.createdOn = createdOn;
+ this.value = value;
+ }
+
+ public Long getQuestionId() {
+ return questionId;
+ }
+
+ public void setQuestionId(Long questionId) {
+ this.questionId = questionId;
+ }
+
+ public Long getResponseId() {
+ return responseId;
+ }
+
+ public void setResponseId(Long responseId) {
+ this.responseId = responseId;
+ }
+
+ public Long getStaffId() {
+ return staffId;
+ }
+
+ public void setStaffId(Long staffId) {
+ this.staffId = staffId;
+ }
+
+ public Long getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(Long clientId) {
+ this.clientId = clientId;
+ }
+
+ public Date getCreatedOn() {
+ return createdOn;
+ }
+
+ public void setCreatedOn(Date createdOn) {
+ this.createdOn = createdOn;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public void setValue(Integer value) {
+ this.value = value;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/data/SurveyData.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/SurveyData.java
new file mode 100644
index 000000000..1cee8ee07
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/data/SurveyData.java
@@ -0,0 +1,113 @@
+/**
+ * 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.spm.data;
+
+import java.util.Date;
+import java.util.List;
+
+public class SurveyData {
+
+ private Long id;
+ private List componentDatas;
+ private List questionDatas;
+ private String key;
+ private String name;
+ private String description;
+ private String countryCode;
+ private Date validFrom;
+ private Date validTo;
+
+ public SurveyData() {
+ super();
+ }
+
+ public SurveyData(final Long id, final List componentDatas, final List questionDatas,
+ final String key, final String name, final String description, final String countryCode,
+ final Date validFrom, final Date validTo) {
+ super();
+ this.id = id;
+ this.componentDatas = componentDatas;
+ this.questionDatas = questionDatas;
+ this.key = key;
+ this.name = name;
+ this.description = description;
+ this.countryCode = countryCode;
+ this.validFrom = validFrom;
+ this.validTo = validTo;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public List getComponentDatas() {
+ return componentDatas;
+ }
+
+ public void setComponentDatas(List componentDatas) {
+ this.componentDatas = componentDatas;
+ }
+
+ public List getQuestionDatas() {
+ return questionDatas;
+ }
+
+ public void setQuestionDatas(List questionDatas) {
+ this.questionDatas = questionDatas;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ public Date getValidFrom() {
+ return validFrom;
+ }
+
+ public void setValidFrom(Date validFrom) {
+ this.validFrom = validFrom;
+ }
+
+ public Date getValidTo() {
+ return validTo;
+ }
+
+ public void setValidTo(Date validTo) {
+ this.validTo = validTo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Component.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Component.java
new file mode 100644
index 000000000..4a21a7918
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Component.java
@@ -0,0 +1,75 @@
+/**
+ * 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.spm.domain;
+
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "m_survey_components")
+public class Component extends AbstractPersistable {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "survey_id")
+ private Survey survey;
+
+ @Column(name = "a_key", length = 32)
+ private String key;
+
+ @Column(name = "a_text", length = 255)
+ private String text;
+
+ @Column(name = "description", length = 4096)
+ private String description;
+
+ @Column(name = "sequence_no", precision = 4)
+ private Integer sequenceNo;
+
+ public Component() {
+ super();
+ }
+
+ public Survey getSurvey() {
+ return survey;
+ }
+
+ public void setSurvey(Survey survey) {
+ this.survey = survey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String title) {
+ this.text = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/LookupTable.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/LookupTable.java
new file mode 100644
index 000000000..f33e67e66
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/LookupTable.java
@@ -0,0 +1,86 @@
+/**
+ * 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.spm.domain;
+
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "m_survey_lookup_tables")
+public class LookupTable extends AbstractPersistable {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "survey_id")
+ private Survey survey;
+
+ @Column(name = "a_key", length = 32)
+ private String key;
+
+ @Column(name = "description", length = 4096)
+ private String description;
+
+ @Column(name = "value_from", precision = 4)
+ private Integer valueFrom;
+
+ @Column(name = "value_to", precision = 4)
+ private Integer valueTo;
+
+ @Column(name = "score", precision = 5, scale = 2)
+ private Double score;
+
+ public LookupTable() {
+ super();
+ }
+
+ public Survey getSurvey() {
+ return survey;
+ }
+
+ public void setSurvey(Survey survey) {
+ this.survey = survey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getValueFrom() {
+ return valueFrom;
+ }
+
+ public void setValueFrom(Integer valueFrom) {
+ this.valueFrom = valueFrom;
+ }
+
+ public Integer getValueTo() {
+ return valueTo;
+ }
+
+ public void setValueTo(Integer valueTo) {
+ this.valueTo = valueTo;
+ }
+
+ public Double getScore() {
+ return score;
+ }
+
+ public void setScore(Double score) {
+ this.score = score;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Question.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Question.java
new file mode 100644
index 000000000..0d4c3c36f
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Question.java
@@ -0,0 +1,99 @@
+/**
+ * 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.spm.domain;
+
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+import java.util.List;
+
+@Entity
+@Table(name = "m_survey_questions")
+public class Question extends AbstractPersistable {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "survey_id")
+ private Survey survey;
+
+ @OneToMany(mappedBy = "question", fetch = FetchType.EAGER)
+ @OrderBy("sequenceNo")
+ private List responses;
+
+ @Column(name = "component_key", length = 32)
+ private String componentKey;
+
+ @Column(name = "a_key", length = 32)
+ private String key;
+
+ @Column(name = "a_text", length = 255)
+ private String text;
+
+ @Column(name = "description", length = 4096)
+ private String description;
+
+ @Column(name = "sequence_no", precision = 4)
+ private Integer sequenceNo;
+
+ public Question() {
+ super();
+ }
+
+ public Survey getSurvey() {
+ return survey;
+ }
+
+ public void setSurvey(Survey survey) {
+ this.survey = survey;
+ }
+
+ public List getResponses() {
+ return responses;
+ }
+
+ public void setResponses(List responses) {
+ this.responses = responses;
+ }
+
+ public String getComponentKey() {
+ return componentKey;
+ }
+
+ public void setComponentKey(String componentKey) {
+ this.componentKey = componentKey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Response.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Response.java
new file mode 100644
index 000000000..93182f2ee
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Response.java
@@ -0,0 +1,64 @@
+/**
+ * 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.spm.domain;
+
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "m_survey_responses")
+public class Response extends AbstractPersistable {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "question_id")
+ private Question question;
+
+ @Column(name = "a_text", length = 255)
+ private String text;
+
+ @Column(name = "a_value", precision = 4)
+ private Integer value;
+
+ @Column(name = "sequence_no", precision = 4)
+ private Integer sequenceNo;
+
+ public Response() {
+ super();
+ }
+
+ public Question getQuestion() {
+ return question;
+ }
+
+ public void setQuestion(Question question) {
+ this.question = question;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public void setValue(Integer value) {
+ this.value = value;
+ }
+
+ public Integer getSequenceNo() {
+ return sequenceNo;
+ }
+
+ public void setSequenceNo(Integer sequenceNo) {
+ this.sequenceNo = sequenceNo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Scorecard.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Scorecard.java
new file mode 100644
index 000000000..5069ed8ac
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Scorecard.java
@@ -0,0 +1,106 @@
+/**
+ * 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.spm.domain;
+
+import org.mifosplatform.organisation.staff.domain.Staff;
+import org.mifosplatform.portfolio.client.domain.Client;
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+import java.util.Date;
+
+@Entity
+@Table(name = "m_survey_scorecards")
+public class Scorecard extends AbstractPersistable {
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "survey_id")
+ private Survey survey;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "question_id")
+ private Question question;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "response_id")
+ private Response response;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "staff_id")
+ private Staff staff;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "client_id")
+ private Client client;
+
+ @Column(name = "created_on")
+ @Temporal(value = TemporalType.TIMESTAMP)
+ @OrderBy("createdOn DESC")
+ private Date createdOn;
+
+ @Column(name = "a_value", precision = 4)
+ private Integer value;
+
+ public Scorecard() {
+ super();
+ }
+
+ public Survey getSurvey() {
+ return survey;
+ }
+
+ public void setSurvey(Survey survey) {
+ this.survey = survey;
+ }
+
+ public Question getQuestion() {
+ return question;
+ }
+
+ public void setQuestion(Question question) {
+ this.question = question;
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ public void setResponse(Response response) {
+ this.response = response;
+ }
+
+ public Staff getStaff() {
+ return staff;
+ }
+
+ public void setStaff(Staff staff) {
+ this.staff = staff;
+ }
+
+ public Client getClient() {
+ return client;
+ }
+
+ public void setClient(Client client) {
+ this.client = client;
+ }
+
+ public Date getCreatedOn() {
+ return createdOn;
+ }
+
+ public void setCreatedOn(Date createdOn) {
+ this.createdOn = createdOn;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public void setValue(Integer value) {
+ this.value = value;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Survey.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Survey.java
new file mode 100644
index 000000000..4d61786f0
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/domain/Survey.java
@@ -0,0 +1,113 @@
+/**
+ * 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.spm.domain;
+
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+import javax.persistence.*;
+import java.util.Date;
+import java.util.List;
+
+@Entity
+@Table(name = "m_surveys")
+public class Survey extends AbstractPersistable {
+
+ @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER)
+ @OrderBy("sequenceNo")
+ private List components;
+
+ @OneToMany(mappedBy = "survey", fetch = FetchType.EAGER)
+ @OrderBy("sequenceNo")
+ private List questions;
+
+ @Column(name = "a_key", length = 32)
+ private String key;
+
+ @Column(name = "a_name", length = 255)
+ private String name;
+
+ @Column(name = "description", length = 4096)
+ private String description;
+
+ @Column(name = "country_code", length = 2)
+ private String countryCode;
+
+ @Column(name = "valid_from")
+ @Temporal(value = TemporalType.TIMESTAMP)
+ private Date validFrom;
+
+ @Column(name = "valid_to")
+ @Temporal(value = TemporalType.TIMESTAMP)
+ private Date validTo;
+
+ public Survey() {
+ super();
+ }
+
+ public List getComponents() {
+ return components;
+ }
+
+ public void setComponents(List components) {
+ this.components = components;
+ }
+
+ public List getQuestions() {
+ return questions;
+ }
+
+ public void setQuestions(List questions) {
+ this.questions = questions;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCountryCode() {
+ return countryCode;
+ }
+
+ public void setCountryCode(String countryCode) {
+ this.countryCode = countryCode;
+ }
+
+ public Date getValidFrom() {
+ return validFrom;
+ }
+
+ public void setValidFrom(Date validFrom) {
+ this.validFrom = validFrom;
+ }
+
+ public Date getValidTo() {
+ return validTo;
+ }
+
+ public void setValidTo(Date validTo) {
+ this.validTo = validTo;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/LookupTableNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/LookupTableNotFoundException.java
new file mode 100644
index 000000000..560d147df
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/LookupTableNotFoundException.java
@@ -0,0 +1,15 @@
+/**
+ * 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.spm.exception;
+
+import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+public class LookupTableNotFoundException extends AbstractPlatformResourceNotFoundException {
+
+ public LookupTableNotFoundException(final Long id) {
+ super("error.msg.survey.lookuptable.id.notfound", "Lookup table with id " + id + " not found!", id);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/SurveyNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/SurveyNotFoundException.java
new file mode 100644
index 000000000..02598e625
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/exception/SurveyNotFoundException.java
@@ -0,0 +1,15 @@
+/**
+ * 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.spm.exception;
+
+import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+public class SurveyNotFoundException extends AbstractPlatformResourceNotFoundException {
+
+ public SurveyNotFoundException(final Long id) {
+ super("error.msg.survey.id.notfound", "Survey with id " + id + " not found!", id);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/LookupTableRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/LookupTableRepository.java
new file mode 100644
index 000000000..5b0e78dd1
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/LookupTableRepository.java
@@ -0,0 +1,17 @@
+/**
+ * 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.spm.repository;
+
+import org.mifosplatform.spm.domain.LookupTable;
+import org.mifosplatform.spm.domain.Survey;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface LookupTableRepository extends JpaRepository {
+
+ List findBySurvey(final Survey survey);
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/ScorecardRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/ScorecardRepository.java
new file mode 100644
index 000000000..5076fabe0
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/ScorecardRepository.java
@@ -0,0 +1,19 @@
+/**
+ * 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.spm.repository;
+
+import org.mifosplatform.portfolio.client.domain.Client;
+import org.mifosplatform.spm.domain.Scorecard;
+import org.mifosplatform.spm.domain.Survey;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface ScorecardRepository extends JpaRepository {
+
+ List findBySurvey(final Survey survey);
+ List findBySurveyAndClient(final Survey survey, final Client client);
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/SurveyRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/SurveyRepository.java
new file mode 100644
index 000000000..8e644a90b
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/repository/SurveyRepository.java
@@ -0,0 +1,22 @@
+/**
+ * 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.spm.repository;
+
+import org.mifosplatform.spm.domain.Survey;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.Date;
+import java.util.List;
+
+public interface SurveyRepository extends JpaRepository {
+
+ @Query("select s from Survey s where :pointInTime between s.validFrom and s.validTo")
+ List fetchActiveSurveys(@Param("pointInTime") final Date pointInTime);
+
+ Survey findByKey(final String key);
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/service/LookupTableService.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/LookupTableService.java
new file mode 100644
index 000000000..6b62fe86e
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/LookupTableService.java
@@ -0,0 +1,48 @@
+/**
+ * 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.spm.service;
+
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.spm.domain.LookupTable;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.repository.LookupTableRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class LookupTableService {
+
+ private final PlatformSecurityContext securityContext;
+ private final LookupTableRepository lookupTableRepository;
+
+ @Autowired
+ public LookupTableService(final PlatformSecurityContext securityContext,
+ final LookupTableRepository lookupTableRepository) {
+ super();
+ this.securityContext = securityContext;
+ this.lookupTableRepository = lookupTableRepository;
+ }
+
+ public LookupTable findById(final Long id) {
+ this.securityContext.authenticatedUser();
+
+ return this.lookupTableRepository.findOne(id);
+ }
+
+ public List findBySurvey(final Survey survey) {
+ this.securityContext.authenticatedUser();
+
+ return this.lookupTableRepository.findBySurvey(survey);
+ }
+
+ public LookupTable createLookupTable(final LookupTable lookupTable) {
+ this.securityContext.authenticatedUser();
+
+ return this.lookupTableRepository.save(lookupTable);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/service/ScorecardService.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/ScorecardService.java
new file mode 100644
index 000000000..d19e0fb70
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/ScorecardService.java
@@ -0,0 +1,51 @@
+/**
+ * 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.spm.service;
+
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.portfolio.client.domain.Client;
+import org.mifosplatform.spm.domain.Scorecard;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.repository.ScorecardRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class ScorecardService {
+
+ private final PlatformSecurityContext securityContext;
+ private final ScorecardRepository scorecardRepository;
+
+ @Autowired
+ public ScorecardService(final PlatformSecurityContext securityContext,
+ final ScorecardRepository scorecardRepository) {
+ super();
+ this.securityContext = securityContext;
+ this.scorecardRepository = scorecardRepository;
+ }
+
+ @Transactional
+ public Scorecard createScorecard(final Scorecard scorecard) {
+ this.securityContext.authenticatedUser();
+
+ return this.scorecardRepository.save(scorecard);
+ }
+
+ public List findBySurvey(final Survey survey) {
+ this.securityContext.authenticatedUser();
+
+ return this.scorecardRepository.findBySurvey(survey);
+ }
+
+ public List findBySurveyAndClient(final Survey survey, final Client client) {
+ this.securityContext.authenticatedUser();
+
+ return this.scorecardRepository.findBySurveyAndClient(survey, client);
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/service/SpmService.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/SpmService.java
new file mode 100644
index 000000000..7d884f91f
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/service/SpmService.java
@@ -0,0 +1,100 @@
+/**
+ * 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.spm.service;
+
+import org.joda.time.DateTime;
+import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
+import org.mifosplatform.spm.domain.Survey;
+import org.mifosplatform.spm.repository.SurveyRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class SpmService {
+
+ private final PlatformSecurityContext securityContext;
+ private final SurveyRepository surveyRepository;
+
+ @Autowired
+ public SpmService(final PlatformSecurityContext securityContext,
+ final SurveyRepository surveyRepository) {
+ super();
+ this.securityContext = securityContext;
+ this.surveyRepository = surveyRepository;
+ }
+
+ public List fetchValidSurveys() {
+ this.securityContext.authenticatedUser();
+
+ return this.surveyRepository.fetchActiveSurveys(new Date());
+ }
+
+ public Survey findById(final Long id) {
+ this.securityContext.authenticatedUser();
+
+ return this.surveyRepository.findOne(id);
+ }
+
+ @Transactional
+ public Survey createSurvey(final Survey survey) {
+ this.securityContext.authenticatedUser();
+
+ final Survey previousSurvey = this.surveyRepository.findByKey(survey.getKey());
+
+ if (previousSurvey != null) {
+ this.deactivateSurvey(previousSurvey.getId());
+ }
+
+ // set valid from to start of today
+ final DateTime dateTime = DateTime
+ .now()
+ .withHourOfDay(0)
+ .withMinuteOfHour(0)
+ .withSecondOfMinute(0)
+ .withMillisOfSecond(0);
+
+ survey.setValidFrom(dateTime.toDate());
+
+ // set valid from to end in 100 years
+ dateTime
+ .withDayOfMonth(31)
+ .withMonthOfYear(12)
+ .withHourOfDay(23)
+ .withMinuteOfHour(59)
+ .withSecondOfMinute(59)
+ .withMillisOfSecond(999)
+ .plusYears(100);
+
+ survey.setValidTo(dateTime.toDate());
+
+ return this.surveyRepository.save(survey);
+ }
+
+ @Transactional
+ public void deactivateSurvey(final Long id) {
+ this.securityContext.authenticatedUser();
+
+ final Survey survey = this.surveyRepository.findOne(id);
+
+ if (survey != null) {
+ // set valid to to yesterday night
+ final DateTime dateTime = DateTime
+ .now()
+ .withHourOfDay(23)
+ .withMinuteOfHour(59)
+ .withSecondOfMinute(59)
+ .withMillisOfSecond(999)
+ .minusDays(1);
+ survey.setValidTo(dateTime.toDate());
+
+ this.surveyRepository.save(survey);
+ }
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/util/LookupTableMapper.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/LookupTableMapper.java
new file mode 100644
index 000000000..05f84b148
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/LookupTableMapper.java
@@ -0,0 +1,36 @@
+/**
+ * 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.spm.util;
+
+import org.mifosplatform.spm.data.LookupTableData;
+import org.mifosplatform.spm.domain.LookupTable;
+import org.mifosplatform.spm.domain.Survey;
+
+public class LookupTableMapper {
+
+ private LookupTableMapper() {
+ super();
+ }
+
+ public static LookupTableData map(final LookupTable lookupTable) {
+ final LookupTableData lookupTableData = new LookupTableData(
+ lookupTable.getId(), lookupTable.getKey(), lookupTable.getDescription(), lookupTable.getValueFrom(),
+ lookupTable.getValueFrom(), lookupTable.getScore()
+ );
+ return lookupTableData;
+ }
+
+ public static LookupTable map(final LookupTableData lookupTableData, final Survey survey) {
+ final LookupTable lookupTable = new LookupTable();
+ lookupTable.setSurvey(survey);
+ lookupTable.setKey(lookupTableData.getKey());
+ lookupTable.setDescription(lookupTableData.getDescription());
+ lookupTable.setValueFrom(lookupTableData.getValueFrom());
+ lookupTable.setValueTo(lookupTableData.getValueTo());
+ lookupTable.setScore(lookupTableData.getScore());
+ return lookupTable;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/util/ScorecardMapper.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/ScorecardMapper.java
new file mode 100644
index 000000000..1fbd26dde
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/ScorecardMapper.java
@@ -0,0 +1,59 @@
+/**
+ * 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.spm.util;
+
+import org.mifosplatform.organisation.staff.domain.Staff;
+import org.mifosplatform.portfolio.client.domain.Client;
+import org.mifosplatform.spm.data.ScorecardData;
+import org.mifosplatform.spm.domain.Question;
+import org.mifosplatform.spm.domain.Response;
+import org.mifosplatform.spm.domain.Scorecard;
+import org.mifosplatform.spm.domain.Survey;
+
+import java.util.List;
+
+public class ScorecardMapper {
+
+ private ScorecardMapper() {
+ super();
+ }
+
+ public static ScorecardData map(final Scorecard scorecard) {
+ final ScorecardData scorecardData = new ScorecardData(
+ scorecard.getQuestion().getId(), scorecard.getResponse().getId(), scorecard.getStaff().getId(),
+ scorecard.getClient().getId(), scorecard.getCreatedOn(), scorecard.getValue()
+ );
+ return scorecardData;
+ }
+
+ public static Scorecard map(final ScorecardData scorecardData, final Survey survey,
+ final Staff staff, final Client client) {
+ final Scorecard scorecard = new Scorecard();
+ ScorecardMapper.setQuestionAndResponse(scorecardData, scorecard, survey);
+ scorecard.setStaff(staff);
+ scorecard.setClient(client);
+ scorecard.setCreatedOn(scorecardData.getCreatedOn());
+ scorecard.setValue(scorecardData.getValue());
+ return scorecard;
+ }
+
+ private static void setQuestionAndResponse(final ScorecardData scorecardData, final Scorecard scorecard,
+ final Survey survey) {
+ final List questions = survey.getQuestions();
+ for (final Question question : questions) {
+ if (question.getId().equals(scorecardData.getQuestionId())) {
+ scorecard.setQuestion(question);
+ for (final Response response : question.getResponses()) {
+ if (response.getId().equals(scorecardData.getResponseId())) {
+ scorecard.setResponse(response);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/spm/util/SurveyMapper.java b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/SurveyMapper.java
new file mode 100644
index 000000000..a2dcf5c00
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/spm/util/SurveyMapper.java
@@ -0,0 +1,130 @@
+/**
+ * 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.spm.util;
+
+import org.mifosplatform.spm.data.ComponentData;
+import org.mifosplatform.spm.data.QuestionData;
+import org.mifosplatform.spm.data.ResponseData;
+import org.mifosplatform.spm.data.SurveyData;
+import org.mifosplatform.spm.domain.Component;
+import org.mifosplatform.spm.domain.Question;
+import org.mifosplatform.spm.domain.Response;
+import org.mifosplatform.spm.domain.Survey;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SurveyMapper {
+
+ private SurveyMapper() {
+ super();
+ }
+
+ public static SurveyData map(final Survey survey) {
+ final SurveyData surveyData = new SurveyData(
+ survey.getId(), SurveyMapper.mapComponents(survey.getComponents()),
+ SurveyMapper.mapQuestions(survey.getQuestions()), survey.getKey(), survey.getName(),
+ survey.getDescription(), survey.getCountryCode(), survey.getValidFrom(), survey.getValidTo()
+ );
+ return surveyData;
+ }
+
+ public static Survey map(final SurveyData surveyData) {
+ final Survey survey = new Survey();
+ survey.setComponents(SurveyMapper.mapComponentDatas(surveyData.getComponentDatas()));
+ survey.setQuestions(SurveyMapper.mapQuestionDatas(surveyData.getQuestionDatas(), survey));
+ survey.setKey(surveyData.getKey());
+ survey.setName(surveyData.getName());
+ survey.setDescription(surveyData.getDescription());
+ survey.setCountryCode(surveyData.getCountryCode());
+ return survey;
+ }
+
+ private static List mapComponents(final List components) {
+ final List componentDatas = new ArrayList<>();
+ if (components != null) {
+ for (final Component component : components) {
+ componentDatas.add(new ComponentData(
+ component.getKey(), component.getText(), component.getDescription(), component.getSequenceNo()
+ ));
+ }
+ }
+ return componentDatas;
+ }
+
+ private static List mapComponentDatas(final List componentDatas) {
+ final List components = new ArrayList<>();
+ if (componentDatas != null) {
+ for (final ComponentData componentData : componentDatas) {
+ final Component component = new Component();
+ component.setKey(componentData.getKey());
+ component.setText(componentData.getText());
+ component.setDescription(componentData.getDescription());
+ component.setSequenceNo(componentData.getSequenceNo());
+ components.add(component);
+ }
+ }
+ return components;
+ }
+
+ private static List mapQuestions(final List questions) {
+ final List questionDatas = new ArrayList<>();
+ if (questions != null) {
+ for (final Question question : questions) {
+ questionDatas.add(new QuestionData(
+ SurveyMapper.mapResponses(question.getResponses()), question.getComponentKey(), question.getKey(),
+ question.getText(), question.getDescription(), question.getSequenceNo()
+ ));
+ }
+ }
+ return questionDatas;
+ }
+
+ private static List mapQuestionDatas(final List questionDatas, final Survey survey) {
+ final List questions = new ArrayList<>();
+ if (questionDatas != null) {
+ for (final QuestionData questionData : questionDatas) {
+ final Question question = new Question();
+ question.setSurvey(survey);
+ question.setComponentKey(questionData.getComponentKey());
+ question.setResponses(SurveyMapper.mapResponseDatas(questionData.getResponseDatas(), question));
+ question.setKey(questionData.getKey());
+ question.setText(questionData.getText());
+ question.setDescription(question.getDescription());
+ question.setSequenceNo(questionData.getSequenceNo());
+ questions.add(question);
+ }
+ }
+ return questions;
+ }
+
+ private static List mapResponses(final List responses) {
+ final List responseDatas = new ArrayList<>();
+ if (responses != null) {
+ for (final Response response : responses) {
+ responseDatas.add(new ResponseData(
+ response.getText(), response.getValue(), response.getSequenceNo()
+ ));
+ }
+ }
+ return responseDatas;
+ }
+
+ private static List mapResponseDatas(final List responseDatas, final Question question) {
+ final List responses = new ArrayList<>();
+ if (responseDatas != null) {
+ for (final ResponseData responseData : responseDatas) {
+ final Response response = new Response();
+ response.setQuestion(question);
+ response.setText(responseData.getText());
+ response.setValue(responseData.getValue());
+ response.setSequenceNo(responseData.getSequenceNo());
+ responses.add(response);
+ }
+ }
+ return responses;
+ }
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java b/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java
index 41876d605..2948b34a5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/template/service/TemplateMergeService.java
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Map.Entry;
import org.codehaus.jackson.map.ObjectMapper;
+import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
import org.mifosplatform.template.domain.Template;
import org.mifosplatform.template.domain.TemplateFunctions;
import org.slf4j.Logger;
@@ -38,6 +39,7 @@ import com.github.mustachejava.MustacheFactory;
@Service
public class TemplateMergeService {
private final static Logger logger = LoggerFactory.getLogger(TemplateMergeService.class);
+
// private final FromJsonHelper fromApiJsonHelper;
private Map scopes;
@@ -46,16 +48,18 @@ public class TemplateMergeService {
// @Autowired
// public TemplateMergeService(final FromJsonHelper fromApiJsonHelper) {
// this.fromApiJsonHelper = fromApiJsonHelper;
- // }
+ //
public void setAuthToken(final String authToken) {
- this.authToken = authToken;
+ //final String auth = ThreadLocalContextUtil.getAuthToken();
+ this.authToken = authToken;
}
+
public String compile(final Template template, final Map scopes) throws MalformedURLException, IOException {
this.scopes = scopes;
this.scopes.put("static", new TemplateFunctions());
-
+
final MustacheFactory mf = new DefaultMustacheFactory();
final Mustache mustache = mf.compile(new StringReader(template.getText()), template.getName());
@@ -169,6 +173,8 @@ public class TemplateMergeService {
private void expandMapArrays(Object value) {
if (value instanceof Map) {
Map valueAsMap = (Map) value;
+ //Map newValue = null;
+ Map valueAsMap_second = new HashMap<>();
for (Entry valueAsMapEntry : valueAsMap.entrySet()) {
Object valueAsMapEntryValue = valueAsMapEntry.getValue();
if (valueAsMapEntryValue instanceof Map) { // JSON Object
@@ -178,12 +184,16 @@ public class TemplateMergeService {
String valueAsMapEntryKey = valueAsMapEntry.getKey();
int i = 0;
for (Object object : valueAsMapEntryValueIterable) {
- valueAsMap.put(valueAsMapEntryKey + "#" + i, object);
+ valueAsMap_second.put(valueAsMapEntryKey + "#" + i, object);
++i;
expandMapArrays(object);
+
}
}
+
}
+ valueAsMap.putAll(valueAsMap_second);
+
}
}
diff --git a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml
index 26e8abead..7b864d0d5 100644
--- a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml
+++ b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml
@@ -85,4 +85,5 @@
+
diff --git a/mifosng-provider/src/main/resources/META-INF/spring/spmContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/spmContext.xml
new file mode 100644
index 000000000..71a37b35d
--- /dev/null
+++ b/mifosng-provider/src/main/resources/META-INF/spring/spmContext.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V280__spm_framework_initial_tables.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V280__spm_framework_initial_tables.sql
new file mode 100644
index 000000000..478df21aa
--- /dev/null
+++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V280__spm_framework_initial_tables.sql
@@ -0,0 +1,72 @@
+CREATE TABLE `m_surveys` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `a_key` VARCHAR(32) NOT NULL,
+ `a_name` VARCHAR(255) NOT NULL,
+ `description` VARCHAR(4000) NULL,
+ `country_code` VARCHAR(2) NOT NULL,
+ `valid_from` DATETIME NULL DEFAULT NULL,
+ `valid_to` DATETIME NULL DEFAULT NULL,
+ PRIMARY KEY (`id`)
+);
+
+CREATE TABLE `m_survey_components` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `survey_id` BIGINT(20) NOT NULL,
+ `a_key` VARCHAR(32) NOT NULL,
+ `a_text` VARCHAR(255) NOT NULL,
+ `description` VARCHAR(4000) NULL,
+ `sequence_no` INT(4) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`survey_id`) REFERENCES `m_surveys` (`id`)
+);
+
+CREATE TABLE `m_survey_questions` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `survey_id` BIGINT(20) NOT NULL,
+ `component_key` VARCHAR(32) NULL,
+ `a_key` VARCHAR(32) NOT NULL,
+ `a_text` VARCHAR(255) NOT NULL,
+ `description` VARCHAR(4000) NULL,
+ `sequence_no` INT(4) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`survey_id`) REFERENCES `m_surveys` (`id`)
+);
+
+CREATE TABLE `m_survey_responses` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `question_id` BIGINT(20) NOT NULL,
+ `a_text` VARCHAR(255) NOT NULL,
+ `a_value` INT(4) NOT NULL,
+ `sequence_no` INT(4) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`question_id`) REFERENCES `m_survey_questions` (`id`)
+);
+
+CREATE TABLE `m_survey_lookup_tables` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `survey_id` BIGINT(20) NOT NULL,
+ `a_key` VARCHAR(255) NOT NULL,
+ `description` INT(4) NULL,
+ `value_from` INT(4) NOT NULL,
+ `value_to` INT(4) NOT NULL,
+ `score` DECIMAL(5, 2) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`survey_id`) REFERENCES `m_surveys` (`id`)
+);
+
+CREATE TABLE `m_survey_scorecards` (
+ `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+ `survey_id` BIGINT(20) NOT NULL,
+ `question_id` BIGINT(20) NOT NULL,
+ `response_id` BIGINT(20) NOT NULL,
+ `staff_id` BIGINT(20) NOT NULL,
+ `client_id` BIGINT(20) NOT NULL,
+ `created_on` DATETIME NULL DEFAULT NULL,
+ `a_value` INT(4) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`survey_id`) REFERENCES `m_surveys` (`id`),
+ FOREIGN KEY (`question_id`) REFERENCES `m_survey_questions` (`id`),
+ FOREIGN KEY (`response_id`) REFERENCES `m_survey_responses` (`id`),
+ FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`),
+ FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`)
+);
\ No newline at end of file
diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V281__add_configuration_param_backdate-penalties.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V281__add_configuration_param_backdate-penalties.sql
new file mode 100644
index 000000000..a4fcb5403
--- /dev/null
+++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V281__add_configuration_param_backdate-penalties.sql
@@ -0,0 +1,4 @@
+INSERT INTO `c_configuration` ( `name`, `value`, `enabled`, `description`)
+VALUES
+ ( 'backdate-penalties-enabled', 0, 1, 'If this parameter is disabled penalties will only be added to instalments due moving forward, any old overdue instalments will not be affected.');
+
diff --git a/mifosng-provider/src/main/resources/sql/migrations/sample_data/barebones_db.sql b/mifosng-provider/src/main/resources/sql/migrations/sample_data/barebones_db.sql
index 074d2b0d7..9ef3a72c9 100644
--- a/mifosng-provider/src/main/resources/sql/migrations/sample_data/barebones_db.sql
+++ b/mifosng-provider/src/main/resources/sql/migrations/sample_data/barebones_db.sql
@@ -246,7 +246,7 @@ CREATE TABLE IF NOT EXISTS `c_configuration` (
`description` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
-) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
-- Dumping data for table mifostenant-default.c_configuration: ~19 rows (approximately)
/*!40000 ALTER TABLE `c_configuration` DISABLE KEYS */;
@@ -269,7 +269,8 @@ INSERT INTO `c_configuration` (`id`, `name`, `value`, `enabled`, `is_trap_door`,
(20, 'office-specific-products-enabled', 0, 0, 0, 'Whether products and fees should be office specific or not? This property should NOT be changed once Mifos is Live.'),
(21, 'restrict-products-to-user-office', 0, 0, 0, 'This should be enabled only if, products & fees are office specific (i.e. office-specific-products-enabled is enabled). This property specifies if the products should be auto-restricted to office of the user who created the proudct? Note: This property should NOT be changed once Mifos is Live.'),
(22, 'office-opening-balances-contra-account', 0, 1, 0, NULL),
- (23, 'rounding-mode', 6, 1, 1, '0 - UP, 1 - DOWN, 2- CEILING, 3- FLOOR, 4- HALF_UP, 5- HALF_DOWN, 6 - HALF_EVEN');
+ (23, 'rounding-mode', 6, 1, 1, '0 - UP, 1 - DOWN, 2- CEILING, 3- FLOOR, 4- HALF_UP, 5- HALF_DOWN, 6 - HALF_EVEN'),
+ (24, 'backdate-penalties-enabled', 0, 1, 0, 'If this parameter is disabled penalties will only be added to instalments due moving forward, any old overdue instalments will not be affected.');
/*!40000 ALTER TABLE `c_configuration` ENABLE KEYS */;
@@ -339,25 +340,25 @@ CREATE TABLE IF NOT EXISTS `job` (
-- Dumping data for table mifostenant-default.job: ~19 rows (approximately)
/*!40000 ALTER TABLE `job` DISABLE KEYS */;
INSERT INTO `job` (`id`, `name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES
- (1, 'Update loan Summary', 'Update loan Summary', '0 0 22 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-11-18 22:00:00', 'Update loan SummaryJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (2, 'Update Loan Arrears Ageing', 'Update Loan Arrears Ageing', '0 1 0 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-11-19 00:01:00', 'Update Loan Arrears AgeingJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (3, 'Update Loan Paid In Advance', 'Update Loan Paid In Advance', '0 5 0 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-11-19 00:05:00', 'Update Loan Paid In AdvanceJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (4, 'Apply Annual Fee For Savings', 'Apply Annual Fee For Savings', '0 20 22 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-11-18 22:20:00', 'Apply Annual Fee For SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (5, 'Apply Holidays To Loans', 'Apply Holidays To Loans', '0 0 12 * * ?', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-11-19 12:00:00', 'Apply Holidays To LoansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (6, 'Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', '2015-06-03 02:56:58', 5, NULL, NULL, '2015-11-19 00:00:00', 'Post Interest For SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
- (7, 'Transfer Fee For Loans From Savings', 'Transfer Fee For Loans From Savings', '0 1 0 1/1 * ? *', '2015-06-03 02:57:00', 5, NULL, NULL, '2015-11-19 00:01:00', 'Transfer Fee For Loans From SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (8, 'Pay Due Savings Charges', 'Pay Due Savings Charges', '0 0 12 * * ?', '2013-09-23 00:00:00', 5, NULL, NULL, '2015-11-19 12:00:00', 'Pay Due Savings ChargesJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (9, 'Update Accounting Running Balances', 'Update Accounting Running Balances', '0 1 0 1/1 * ? *', '2015-06-03 02:57:00', 5, NULL, NULL, '2015-11-19 00:01:00', 'Update Accounting Running BalancesJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (10, 'Execute Standing Instruction', 'Execute Standing Instruction', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-11-19 00:00:00', 'Execute Standing InstructionJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (11, 'Add Accrual Transactions', 'Add Accrual Transactions', '0 1 0 1/1 * ? *', '2015-06-03 02:57:04', 3, NULL, NULL, '2015-11-19 00:01:00', 'Add Accrual TransactionsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (12, 'Apply penalty to overdue loans', 'Apply penalty to overdue loans', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-11-19 00:00:00', 'Apply penalty to overdue loansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (13, 'Update Non Performing Assets', 'Update Non Performing Assets', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-11-19 00:00:00', 'Update Non Performing AssetsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (14, 'Transfer Interest To Savings', 'Transfer Interest To Savings', '0 2 0 1/1 * ? *', '2015-06-03 02:57:05', 4, NULL, NULL, '2015-11-19 00:02:00', 'Transfer Interest To SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
- (15, 'Update Deposit Accounts Maturity details', 'Update Deposit Accounts Maturity details', '0 0 0 1/1 * ? *', '2015-06-03 02:57:05', 5, NULL, NULL, '2015-11-19 00:00:00', 'Update Deposit Accounts Maturity detailsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (16, 'Add Periodic Accrual Transactions', 'Add Periodic Accrual Transactions', '0 2 0 1/1 * ? *', '2015-06-03 02:57:06', 2, NULL, NULL, '2015-11-19 00:02:00', 'Add Periodic Accrual TransactionsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (17, 'Recalculate Interest For Loans', 'Recalculate Interest For Loans', '0 1 0 1/1 * ? *', '2015-06-03 02:57:07', 4, NULL, NULL, '2015-11-19 00:01:00', 'Recalculate Interest For LoansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (18, 'Generate Mandatory Savings Schedule', 'Generate Mandatory Savings Schedule', '0 5 0 1/1 * ? *', '2015-06-03 02:57:12', 5, NULL, NULL, '2015-11-19 00:05:00', 'Generate Mandatory Savings ScheduleJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (19, 'Generate Loan Loss Provisioning', 'Generate Loan Loss Provisioning', '0 0 0 1/1 * ? *', '2015-10-20 19:57:53', 5, NULL, NULL, '2015-11-19 00:00:00', 'Generate Loan Loss ProvisioningJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0);
+ (1, 'Update loan Summary', 'Update loan Summary', '0 0 22 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-12-02 22:00:00', 'Update loan SummaryJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (2, 'Update Loan Arrears Ageing', 'Update Loan Arrears Ageing', '0 1 0 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-12-03 00:01:00', 'Update Loan Arrears AgeingJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (3, 'Update Loan Paid In Advance', 'Update Loan Paid In Advance', '0 5 0 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-12-03 00:05:00', 'Update Loan Paid In AdvanceJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (4, 'Apply Annual Fee For Savings', 'Apply Annual Fee For Savings', '0 20 22 1/1 * ? *', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-12-02 22:20:00', 'Apply Annual Fee For SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (5, 'Apply Holidays To Loans', 'Apply Holidays To Loans', '0 0 12 * * ?', '2015-06-03 02:56:57', 5, NULL, NULL, '2015-12-03 12:00:00', 'Apply Holidays To LoansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (6, 'Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', '2015-06-03 02:56:58', 5, NULL, NULL, '2015-12-03 00:00:00', 'Post Interest For SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
+ (7, 'Transfer Fee For Loans From Savings', 'Transfer Fee For Loans From Savings', '0 1 0 1/1 * ? *', '2015-06-03 02:57:00', 5, NULL, NULL, '2015-12-03 00:01:00', 'Transfer Fee For Loans From SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (8, 'Pay Due Savings Charges', 'Pay Due Savings Charges', '0 0 12 * * ?', '2013-09-23 00:00:00', 5, NULL, NULL, '2015-12-03 12:00:00', 'Pay Due Savings ChargesJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (9, 'Update Accounting Running Balances', 'Update Accounting Running Balances', '0 1 0 1/1 * ? *', '2015-06-03 02:57:00', 5, NULL, NULL, '2015-12-03 00:01:00', 'Update Accounting Running BalancesJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (10, 'Execute Standing Instruction', 'Execute Standing Instruction', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-12-03 00:00:00', 'Execute Standing InstructionJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (11, 'Add Accrual Transactions', 'Add Accrual Transactions', '0 1 0 1/1 * ? *', '2015-06-03 02:57:04', 3, NULL, NULL, '2015-12-03 00:01:00', 'Add Accrual TransactionsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (12, 'Apply penalty to overdue loans', 'Apply penalty to overdue loans', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-12-03 00:00:00', 'Apply penalty to overdue loansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (13, 'Update Non Performing Assets', 'Update Non Performing Assets', '0 0 0 1/1 * ? *', '2015-06-03 02:57:04', 5, NULL, NULL, '2015-12-03 00:00:00', 'Update Non Performing AssetsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (14, 'Transfer Interest To Savings', 'Transfer Interest To Savings', '0 2 0 1/1 * ? *', '2015-06-03 02:57:05', 4, NULL, NULL, '2015-12-03 00:02:00', 'Transfer Interest To SavingsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
+ (15, 'Update Deposit Accounts Maturity details', 'Update Deposit Accounts Maturity details', '0 0 0 1/1 * ? *', '2015-06-03 02:57:05', 5, NULL, NULL, '2015-12-03 00:00:00', 'Update Deposit Accounts Maturity detailsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (16, 'Add Periodic Accrual Transactions', 'Add Periodic Accrual Transactions', '0 2 0 1/1 * ? *', '2015-06-03 02:57:06', 2, NULL, NULL, '2015-12-03 00:02:00', 'Add Periodic Accrual TransactionsJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (17, 'Recalculate Interest For Loans', 'Recalculate Interest For Loans', '0 1 0 1/1 * ? *', '2015-06-03 02:57:07', 4, NULL, NULL, '2015-12-03 00:01:00', 'Recalculate Interest For LoansJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (18, 'Generate Mandatory Savings Schedule', 'Generate Mandatory Savings Schedule', '0 5 0 1/1 * ? *', '2015-06-03 02:57:12', 5, NULL, NULL, '2015-12-03 00:05:00', 'Generate Mandatory Savings ScheduleJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (19, 'Generate Loan Loss Provisioning', 'Generate Loan Loss Provisioning', '0 0 0 1/1 * ? *', '2015-10-20 19:57:53', 5, NULL, NULL, '2015-12-03 00:00:00', 'Generate Loan Loss ProvisioningJobDetail1 _ DEFAULT', NULL, 1, 0, 1, 0, 0);
/*!40000 ALTER TABLE `job` ENABLE KEYS */;
@@ -2825,7 +2826,7 @@ CREATE TABLE IF NOT EXISTS `m_permission` (
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=695 DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-default.m_permission: ~775 rows (approximately)
+-- Dumping data for table mifostenant-default.m_permission: ~570 rows (approximately)
/*!40000 ALTER TABLE `m_permission` DISABLE KEYS */;
INSERT INTO `m_permission` (`id`, `grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
(1, 'special', 'ALL_FUNCTIONS', NULL, NULL, 0),
@@ -4630,7 +4631,7 @@ CREATE TABLE IF NOT EXISTS `schema_version` (
KEY `schema_version_s_idx` (`success`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-default.schema_version: ~371 rows (approximately)
+-- Dumping data for table mifostenant-default.schema_version: ~197 rows (approximately)
/*!40000 ALTER TABLE `schema_version` DISABLE KEYS */;
INSERT INTO `schema_version` (`version_rank`, `installed_rank`, `version`, `description`, `type`, `script`, `checksum`, `installed_by`, `installed_on`, `execution_time`, `success`) VALUES
(1, 1, '1', 'mifosplatform-core-ddl-latest', 'SQL', 'V1__mifosplatform-core-ddl-latest.sql', 1800446512, 'root', '2015-06-03 15:26:50', 919, 1),
@@ -4847,6 +4848,8 @@ INSERT INTO `schema_version` (`version_rank`, `installed_rank`, `version`, `desc
(291, 291, '278', 'LoanTransactionProcessingStrategy', 'SQL', 'V278__LoanTransactionProcessingStrategy.sql', -1388446419, 'root', '2015-11-04 17:03:00', 878, 1),
(292, 292, '279', 'floating rates', 'SQL', 'V279__floating_rates.sql', 830029264, 'root', '2015-11-18 16:13:08', 620, 1),
(28, 28, '28', 'accounting-abstractions-and-autoposting', 'SQL', 'V28__accounting-abstractions-and-autoposting.sql', -626584837, 'root', '2015-06-03 15:26:53', 63, 1),
+ (293, 293, '280', 'spm framework initial tables', 'SQL', 'V280__spm_framework_initial_tables.sql', -1638980235, 'root', '2015-12-02 16:07:43', 509, 1),
+ (294, 294, '281', 'add configuration param backdate-penalties', 'SQL', 'V281__add_configuration_param_backdate-penalties.sql', -45520299, 'root', '2015-12-02 16:07:44', 39, 1),
(29, 29, '29', 'add-support-for-annual-fees-on-savings', 'SQL', 'V29__add-support-for-annual-fees-on-savings.sql', -1595233842, 'root', '2015-06-03 15:26:53', 157, 1),
(3, 3, '3', 'mifosx-permissions-and-authorisation-utf8', 'SQL', 'V3__mifosx-permissions-and-authorisation-utf8.sql', 914436650, 'root', '2015-06-03 15:26:50', 14, 1),
(30, 30, '30', 'add-referenceNumber-to-acc gl journal entry', 'SQL', 'V30__add-referenceNumber-to-acc_gl_journal_entry.sql', 255130282, 'root', '2015-06-03 15:26:53', 59, 1),
@@ -5014,7 +5017,7 @@ CREATE TABLE IF NOT EXISTS `stretchy_report` (
UNIQUE KEY `report_name_UNIQUE` (`report_name`)
) ENGINE=InnoDB AUTO_INCREMENT=165 DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-default.stretchy_report: ~125 rows (approximately)
+-- Dumping data for table mifostenant-default.stretchy_report: ~61 rows (approximately)
/*!40000 ALTER TABLE `stretchy_report` DISABLE KEYS */;
INSERT INTO `stretchy_report` (`id`, `report_name`, `report_type`, `report_subtype`, `report_category`, `report_sql`, `description`, `core_report`, `use_report`) VALUES
(1, 'Client Listing', 'Table', NULL, 'Client', 'select\nconcat(repeat("..",\n ((LENGTH(ounder.`hierarchy`) - LENGTH(REPLACE(ounder.`hierarchy`, \'.\', \'\')) - 1))), ounder.`name`) as "Office/Branch",\n c.account_no as "Client Account No.",\nc.display_name as "Name",\nr.enum_message_property as "Status",\nc.activation_date as "Activation", c.external_id as "External Id"\nfrom m_office o\njoin m_office ounder on ounder.hierarchy like concat(o.hierarchy, \'%\')\nand ounder.hierarchy like concat(\'${currentUserHierarchy}\', \'%\')\njoin m_client c on c.office_id = ounder.id\nleft join r_enum_value r on r.enum_name = \'status_enum\' and r.enum_id = c.status_enum\nwhere o.id = ${officeId}\norder by ounder.hierarchy, c.account_no', 'Individual Client Report\r\n\r\nLists the small number of defined fields on the client table. Would expect to copy this \n\nreport and add any \'one to one\' additional data for specific tenant needs.\r\n\r\nCan be run for any size MFI but you\'d expect it only to be run within a branch for \n\nlarger ones. Depending on how many columns are displayed, there is probably is a limit of about 20/50k clients returned for html display (export to excel doesn\'t \n\nhave that client browser/memory impact).', 1, 1),
diff --git a/mifosng-provider/src/main/resources/sql/migrations/sample_data/load_sample_data.sql b/mifosng-provider/src/main/resources/sql/migrations/sample_data/load_sample_data.sql
index a793dda99..7316cbe75 100644
--- a/mifosng-provider/src/main/resources/sql/migrations/sample_data/load_sample_data.sql
+++ b/mifosng-provider/src/main/resources/sql/migrations/sample_data/load_sample_data.sql
@@ -323,7 +323,7 @@ CREATE TABLE IF NOT EXISTS `c_configuration` (
`description` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
-) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
-- Dumping data for table mifostenant-reference.c_configuration: ~19 rows (approximately)
/*!40000 ALTER TABLE `c_configuration` DISABLE KEYS */;
@@ -346,7 +346,8 @@ INSERT INTO `c_configuration` (`id`, `name`, `value`, `enabled`, `is_trap_door`,
(20, 'office-specific-products-enabled', 0, 0, 0, 'Whether products and fees should be office specific or not? This property should NOT be changed once Mifos is Live.'),
(21, 'restrict-products-to-user-office', 0, 0, 0, 'This should be enabled only if, products & fees are office specific (i.e. office-specific-products-enabled is enabled). This property specifies if the products should be auto-restricted to office of the user who created the proudct? Note: This property should NOT be changed once Mifos is Live.'),
(22, 'office-opening-balances-contra-account', 0, 1, 0, NULL),
- (23, 'rounding-mode', 6, 1, 1, '0 - UP, 1 - DOWN, 2- CEILING, 3- FLOOR, 4- HALF_UP, 5- HALF_DOWN, 6 - HALF_EVEN');
+ (23, 'rounding-mode', 6, 1, 1, '0 - UP, 1 - DOWN, 2- CEILING, 3- FLOOR, 4- HALF_UP, 5- HALF_DOWN, 6 - HALF_EVEN'),
+ (24, 'backdate-penalties-enabled', 0, 1, 0, 'If this parameter is disabled penalties will only be added to instalments due moving forward, any old overdue instalments will not be affected.');
/*!40000 ALTER TABLE `c_configuration` ENABLE KEYS */;
@@ -416,25 +417,25 @@ CREATE TABLE IF NOT EXISTS `job` (
-- Dumping data for table mifostenant-reference.job: ~19 rows (approximately)
/*!40000 ALTER TABLE `job` DISABLE KEYS */;
INSERT INTO `job` (`id`, `name`, `display_name`, `cron_expression`, `create_time`, `task_priority`, `group_name`, `previous_run_start_time`, `next_run_time`, `job_key`, `initializing_errorlog`, `is_active`, `currently_running`, `updates_allowed`, `scheduler_group`, `is_misfired`) VALUES
- (1, 'Update loan Summary', 'Update loan Summary', '0 0 22 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, '2014-06-11 09:30:00', '2015-11-18 22:00:00', 'Update loan SummaryJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (2, 'Update Loan Arrears Ageing', 'Update Loan Arrears Ageing', '0 1 0 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, NULL, '2015-11-19 00:01:00', 'Update Loan Arrears AgeingJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (3, 'Update Loan Paid In Advance', 'Update Loan Paid In Advance', '0 5 0 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, NULL, '2015-11-19 00:05:00', 'Update Loan Paid In AdvanceJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (4, 'Apply Annual Fee For Savings', 'Apply Annual Fee For Savings', '0 20 22 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, '2014-06-11 09:50:00', '2015-11-18 22:20:00', 'Apply Annual Fee For SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (5, 'Apply Holidays To Loans', 'Apply Holidays To Loans', '0 0 12 * * ?', '2014-03-07 18:29:14', 5, NULL, '2014-03-24 12:00:04', '2015-11-19 12:00:00', 'Apply Holidays To LoansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (6, 'Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', '2014-03-07 18:29:21', 5, NULL, NULL, '2015-11-19 00:00:00', 'Post Interest For SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
- (7, 'Transfer Fee For Loans From Savings', 'Transfer Fee For Loans From Savings', '0 1 0 1/1 * ? *', '2014-03-07 18:29:32', 5, NULL, NULL, '2015-11-19 00:01:00', 'Transfer Fee For Loans From SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (8, 'Pay Due Savings Charges', 'Pay Due Savings Charges', '0 0 12 * * ?', '2013-09-23 00:00:00', 5, NULL, '2014-03-24 12:00:04', '2015-11-19 12:00:00', 'Pay Due Savings ChargesJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (9, 'Update Accounting Running Balances', 'Update Accounting Running Balances', '0 1 0 1/1 * ? *', '2014-03-07 18:29:37', 5, NULL, NULL, '2015-11-19 00:01:00', 'Update Accounting Running BalancesJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (10, 'Execute Standing Instruction', 'Execute Standing Instruction', '0 0 0 1/1 * ? *', '2014-05-01 16:10:35', 5, NULL, NULL, '2015-11-19 00:00:00', 'Execute Standing InstructionJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (11, 'Add Accrual Transactions', 'Add Accrual Transactions', '0 1 0 1/1 * ? *', '2014-05-01 16:10:36', 3, NULL, NULL, '2015-11-19 00:01:00', 'Add Accrual TransactionsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (12, 'Apply penalty to overdue loans', 'Apply penalty to overdue loans', '0 0 0 1/1 * ? *', '2014-05-01 16:10:36', 5, NULL, NULL, '2015-11-19 00:00:00', 'Apply penalty to overdue loansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (13, 'Update Non Performing Assets', 'Update Non Performing Assets', '0 0 0 1/1 * ? *', '2014-05-01 16:10:41', 5, NULL, NULL, '2015-11-19 00:00:00', 'Update Non Performing AssetsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (14, 'Transfer Interest To Savings', 'Transfer Interest To Savings', '0 2 0 1/1 * ? *', '2014-06-11 09:09:15', 4, NULL, NULL, '2015-11-19 00:02:00', 'Transfer Interest To SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
- (15, 'Update Deposit Accounts Maturity details', 'Update Deposit Accounts Maturity details', '0 0 0 1/1 * ? *', '2014-06-11 09:09:15', 5, NULL, NULL, '2015-11-19 00:00:00', 'Update Deposit Accounts Maturity detailsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (16, 'Add Periodic Accrual Transactions', 'Add Periodic Accrual Transactions', '0 2 0 1/1 * ? *', '2014-10-14 16:19:45', 2, NULL, NULL, '2015-11-19 00:02:00', 'Add Periodic Accrual TransactionsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (17, 'Recalculate Interest For Loans', 'Recalculate Interest For Loans', '0 1 0 1/1 * ? *', '2014-10-14 16:19:55', 4, NULL, NULL, '2015-11-19 00:01:00', 'Recalculate Interest For LoansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
- (18, 'Generate Mandatory Savings Schedule', 'Generate Mandatory Savings Schedule', '0 5 0 1/1 * ? *', '2015-04-16 02:28:43', 5, NULL, NULL, '2015-11-19 00:05:00', 'Generate Mandatory Savings ScheduleJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
- (19, 'Generate Loan Loss Provisioning', 'Generate Loan Loss Provisioning', '0 0 0 1/1 * ? *', '2015-10-20 19:57:58', 5, NULL, NULL, '2015-11-19 00:00:00', 'Generate Loan Loss ProvisioningJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0);
+ (1, 'Update loan Summary', 'Update loan Summary', '0 0 22 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, '2014-06-11 09:30:00', '2015-12-02 22:00:00', 'Update loan SummaryJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (2, 'Update Loan Arrears Ageing', 'Update Loan Arrears Ageing', '0 1 0 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, NULL, '2015-12-03 00:01:00', 'Update Loan Arrears AgeingJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (3, 'Update Loan Paid In Advance', 'Update Loan Paid In Advance', '0 5 0 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, NULL, '2015-12-03 00:05:00', 'Update Loan Paid In AdvanceJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (4, 'Apply Annual Fee For Savings', 'Apply Annual Fee For Savings', '0 20 22 1/1 * ? *', '2014-03-07 18:29:14', 5, NULL, '2014-06-11 09:50:00', '2015-12-02 22:20:00', 'Apply Annual Fee For SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (5, 'Apply Holidays To Loans', 'Apply Holidays To Loans', '0 0 12 * * ?', '2014-03-07 18:29:14', 5, NULL, '2014-03-24 12:00:04', '2015-12-03 12:00:00', 'Apply Holidays To LoansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (6, 'Post Interest For Savings', 'Post Interest For Savings', '0 0 0 1/1 * ? *', '2014-03-07 18:29:21', 5, NULL, NULL, '2015-12-03 00:00:00', 'Post Interest For SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
+ (7, 'Transfer Fee For Loans From Savings', 'Transfer Fee For Loans From Savings', '0 1 0 1/1 * ? *', '2014-03-07 18:29:32', 5, NULL, NULL, '2015-12-03 00:01:00', 'Transfer Fee For Loans From SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (8, 'Pay Due Savings Charges', 'Pay Due Savings Charges', '0 0 12 * * ?', '2013-09-23 00:00:00', 5, NULL, '2014-03-24 12:00:04', '2015-12-03 12:00:00', 'Pay Due Savings ChargesJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (9, 'Update Accounting Running Balances', 'Update Accounting Running Balances', '0 1 0 1/1 * ? *', '2014-03-07 18:29:37', 5, NULL, NULL, '2015-12-03 00:01:00', 'Update Accounting Running BalancesJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (10, 'Execute Standing Instruction', 'Execute Standing Instruction', '0 0 0 1/1 * ? *', '2014-05-01 16:10:35', 5, NULL, NULL, '2015-12-03 00:00:00', 'Execute Standing InstructionJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (11, 'Add Accrual Transactions', 'Add Accrual Transactions', '0 1 0 1/1 * ? *', '2014-05-01 16:10:36', 3, NULL, NULL, '2015-12-03 00:01:00', 'Add Accrual TransactionsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (12, 'Apply penalty to overdue loans', 'Apply penalty to overdue loans', '0 0 0 1/1 * ? *', '2014-05-01 16:10:36', 5, NULL, NULL, '2015-12-03 00:00:00', 'Apply penalty to overdue loansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (13, 'Update Non Performing Assets', 'Update Non Performing Assets', '0 0 0 1/1 * ? *', '2014-05-01 16:10:41', 5, NULL, NULL, '2015-12-03 00:00:00', 'Update Non Performing AssetsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (14, 'Transfer Interest To Savings', 'Transfer Interest To Savings', '0 2 0 1/1 * ? *', '2014-06-11 09:09:15', 4, NULL, NULL, '2015-12-03 00:02:00', 'Transfer Interest To SavingsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 1, 0),
+ (15, 'Update Deposit Accounts Maturity details', 'Update Deposit Accounts Maturity details', '0 0 0 1/1 * ? *', '2014-06-11 09:09:15', 5, NULL, NULL, '2015-12-03 00:00:00', 'Update Deposit Accounts Maturity detailsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (16, 'Add Periodic Accrual Transactions', 'Add Periodic Accrual Transactions', '0 2 0 1/1 * ? *', '2014-10-14 16:19:45', 2, NULL, NULL, '2015-12-03 00:02:00', 'Add Periodic Accrual TransactionsJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (17, 'Recalculate Interest For Loans', 'Recalculate Interest For Loans', '0 1 0 1/1 * ? *', '2014-10-14 16:19:55', 4, NULL, NULL, '2015-12-03 00:01:00', 'Recalculate Interest For LoansJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 3, 0),
+ (18, 'Generate Mandatory Savings Schedule', 'Generate Mandatory Savings Schedule', '0 5 0 1/1 * ? *', '2015-04-16 02:28:43', 5, NULL, NULL, '2015-12-03 00:05:00', 'Generate Mandatory Savings ScheduleJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0),
+ (19, 'Generate Loan Loss Provisioning', 'Generate Loan Loss Provisioning', '0 0 0 1/1 * ? *', '2015-10-20 19:57:58', 5, NULL, NULL, '2015-12-03 00:00:00', 'Generate Loan Loss ProvisioningJobDetail2 _ DEFAULT', NULL, 1, 0, 1, 0, 0);
/*!40000 ALTER TABLE `job` ENABLE KEYS */;
@@ -2997,7 +2998,7 @@ CREATE TABLE IF NOT EXISTS `m_permission` (
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=695 DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-reference.m_permission: ~706 rows (approximately)
+-- Dumping data for table mifostenant-reference.m_permission: ~741 rows (approximately)
/*!40000 ALTER TABLE `m_permission` DISABLE KEYS */;
INSERT INTO `m_permission` (`id`, `grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
(1, 'special', 'ALL_FUNCTIONS', NULL, NULL, 0),
@@ -3739,7 +3740,7 @@ CREATE TABLE IF NOT EXISTS `m_portfolio_command_source` (
CONSTRAINT `FK_m_maker_m_appuser` FOREIGN KEY (`maker_id`) REFERENCES `m_appuser` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=73 DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-reference.m_portfolio_command_source: ~75 rows (approximately)
+-- Dumping data for table mifostenant-reference.m_portfolio_command_source: ~71 rows (approximately)
/*!40000 ALTER TABLE `m_portfolio_command_source` DISABLE KEYS */;
INSERT INTO `m_portfolio_command_source` (`id`, `action_name`, `entity_name`, `office_id`, `group_id`, `client_id`, `loan_id`, `savings_account_id`, `api_get_url`, `resource_id`, `subresource_id`, `command_as_json`, `maker_id`, `made_on_date`, `checker_id`, `checked_on_date`, `processing_result_enum`, `product_id`, `transaction_id`) VALUES
(1, 'CREATE', 'STAFF', 1, NULL, NULL, NULL, NULL, '/staff/template', 1, NULL, '{"isLoanOfficer":true,"officeId":1,"firstname":"Aliya","lastname":"A"}', 1, '2014-03-07 19:10:05', NULL, NULL, 1, NULL, NULL),
@@ -4887,7 +4888,7 @@ CREATE TABLE IF NOT EXISTS `schema_version` (
KEY `schema_version_s_idx` (`success`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--- Dumping data for table mifostenant-reference.schema_version: ~280 rows (approximately)
+-- Dumping data for table mifostenant-reference.schema_version: ~322 rows (approximately)
/*!40000 ALTER TABLE `schema_version` DISABLE KEYS */;
INSERT INTO `schema_version` (`version_rank`, `installed_rank`, `version`, `description`, `type`, `script`, `checksum`, `installed_by`, `installed_on`, `execution_time`, `success`) VALUES
(1, 1, '1', 'mifosplatform-core-ddl-latest', 'SQL', 'V1__mifosplatform-core-ddl-latest.sql', -1957145051, 'root', '2014-03-08 02:28:38', 10710, 1),
@@ -5104,6 +5105,8 @@ INSERT INTO `schema_version` (`version_rank`, `installed_rank`, `version`, `desc
(291, 291, '278', 'LoanTransactionProcessingStrategy', 'SQL', 'V278__LoanTransactionProcessingStrategy.sql', -1388446419, 'root', '2015-11-04 17:03:01', 877, 1),
(292, 292, '279', 'floating rates', 'SQL', 'V279__floating_rates.sql', 830029264, 'root', '2015-11-18 16:13:09', 645, 1),
(28, 28, '28', 'accounting-abstractions-and-autoposting', 'SQL', 'V28__accounting-abstractions-and-autoposting.sql', -966431980, 'root', '2014-03-08 02:28:53', 556, 1),
+ (293, 293, '280', 'spm framework initial tables', 'SQL', 'V280__spm_framework_initial_tables.sql', -1638980235, 'root', '2015-12-02 16:07:45', 630, 1),
+ (294, 294, '281', 'add configuration param backdate-penalties', 'SQL', 'V281__add_configuration_param_backdate-penalties.sql', -45520299, 'root', '2015-12-02 16:07:45', 45, 1),
(29, 29, '29', 'add-support-for-annual-fees-on-savings', 'SQL', 'V29__add-support-for-annual-fees-on-savings.sql', 992227725, 'root', '2014-03-08 02:28:55', 1556, 1),
(3, 3, '3', 'mifosx-permissions-and-authorisation-utf8', 'SQL', 'V3__mifosx-permissions-and-authorisation-utf8.sql', 1922951887, 'root', '2014-03-08 02:28:38', 110, 1),
(30, 30, '30', 'add-referenceNumber-to-acc gl journal entry', 'SQL', 'V30__add-referenceNumber-to-acc_gl_journal_entry.sql', 2079970797, 'root', '2014-03-08 02:28:55', 327, 1),
@@ -5271,7 +5274,7 @@ CREATE TABLE IF NOT EXISTS `stretchy_report` (
UNIQUE KEY `report_name_UNIQUE` (`report_name`)
) ENGINE=InnoDB AUTO_INCREMENT=165 DEFAULT CHARSET=utf8;
--- Dumping data for table mifostenant-reference.stretchy_report: ~61 rows (approximately)
+-- Dumping data for table mifostenant-reference.stretchy_report: ~125 rows (approximately)
/*!40000 ALTER TABLE `stretchy_report` DISABLE KEYS */;
INSERT INTO `stretchy_report` (`id`, `report_name`, `report_type`, `report_subtype`, `report_category`, `report_sql`, `description`, `core_report`, `use_report`) VALUES
(1, 'Client Listing', 'Table', NULL, 'Client', 'select \nconcat(repeat("..", \n ((LENGTH(ounder.`hierarchy`) - LENGTH(REPLACE(ounder.`hierarchy`, \'.\', \'\')) - 1))), ounder.`name`) as "Office/Branch",\n c.account_no as "Client Account No.", \nc.display_name as "Name", \nr.enum_message_property as "Status",\nc.activation_date as "Activation", c.external_id as "External Id"\nfrom m_office o \njoin m_office ounder on ounder.hierarchy like concat(o.hierarchy, \'%\')\nand ounder.hierarchy like concat(\'${currentUserHierarchy}\', \'%\')\njoin m_client c on c.office_id = ounder.id\nleft join r_enum_value r on r.enum_name = \'status_enum\' and r.enum_id = c.status_enum\nwhere o.id = ${officeId}\norder by ounder.hierarchy, c.account_no', 'Individual Client Report\r\n\r\nLists the small number of defined fields on the client table. Would expect to copy this \n\nreport and add any \'one to one\' additional data for specific tenant needs.\r\n\r\nCan be run for any size MFI but you\'d expect it only to be run within a branch for \n\nlarger ones. Depending on how many columns are displayed, there is probably is a limit of about 20/50k clients returned for html display (export to excel doesn\'t \n\nhave that client browser/memory impact).', 1, 1),