#15: issue experts workflow now very slick
This commit is contained in:
parent
2d4f7a897d
commit
adcdacc6c1
39
README.md
39
README.md
|
@ -43,6 +43,20 @@ If you're thinking of joining in development on this I'd strongly recommend you
|
||||||
You should also read the [User-Oriented Specification](doc/specification/userspec.md) and any other documentation which appears under the *doc/specification* hierarchy.
|
You should also read the [User-Oriented Specification](doc/specification/userspec.md) and any other documentation which appears under the *doc/specification* hierarchy.
|
||||||
|
|
||||||
|
|
||||||
|
## Building this
|
||||||
|
|
||||||
|
This application is built using [Application Description Language](); the intention is that soon Application Description Language will run as a Leiningen plugin, but that does not yet work.
|
||||||
|
|
||||||
|
So first you must check out the Application Description Language repository as well as this repository, ideally within a a common directory;
|
||||||
|
|
||||||
|
then:
|
||||||
|
|
||||||
|
cd adl
|
||||||
|
lein uberjar
|
||||||
|
java -jar target/adl-1.4.4-SNAPSHOT-standalone.jar --path ../youyesyet/ ../youyesyet/youyesyet.adl.xml
|
||||||
|
|
||||||
|
This will generate a large number of the source files required by YouYesYet, **including** the database initialisation scripts.
|
||||||
|
|
||||||
## Getting the database up
|
## Getting the database up
|
||||||
|
|
||||||
You'll need a file *profiles.clj*, with content similar to the following; it's not in the repository because it contains passwords.
|
You'll need a file *profiles.clj*, with content similar to the following; it's not in the repository because it contains passwords.
|
||||||
|
@ -62,8 +76,16 @@ Do get the database initialised, run
|
||||||
I'm no longer using Migratus as I'm using [Application Description Language]()
|
I'm no longer using Migratus as I'm using [Application Description Language]()
|
||||||
to generate the majority of the application, and, as changes are made to the application
|
to generate the majority of the application, and, as changes are made to the application
|
||||||
description, new database schemas are generated. The database initialisation script will
|
description, new database schemas are generated. The database initialisation script will
|
||||||
be found at `resources/sql/youyesyet.postgres.sql`. Reference data initialisation scripts
|
be found at `resources/sql/youyesyet.postgres.sql`. Manually maintained overrides are found in
|
||||||
will in due course be stored in the same directory.
|
`resources/sql/youyesyet.postgres.overrides.sql`. So to initialise the database, invoke
|
||||||
|
|
||||||
|
psql youyesyet_dev < resources/sql/youyesyet.postgres.sql
|
||||||
|
|
||||||
|
followed by
|
||||||
|
|
||||||
|
psql youyesyet_dev < resources/sql/youyesyet.postgres.overrides.sql
|
||||||
|
|
||||||
|
Reference data initialisation scripts will in due course be stored in the same directory.
|
||||||
|
|
||||||
Once we have a more or less finished application it may be worth going back to
|
Once we have a more or less finished application it may be worth going back to
|
||||||
[Migratus](https://github.com/yogthos/migratus); I might have a go at generating migrations from
|
[Migratus](https://github.com/yogthos/migratus); I might have a go at generating migrations from
|
||||||
|
@ -75,20 +97,25 @@ To run in a dev environment, checkout the *develop* branch
|
||||||
|
|
||||||
To download and install Javascript delendencies, run
|
To download and install Javascript delendencies, run
|
||||||
|
|
||||||
|
cd youyesyet
|
||||||
lein npm install
|
lein npm install
|
||||||
|
|
||||||
To start a development web server for the application, run:
|
To start a development web server for the application, run:
|
||||||
|
|
||||||
lein run
|
Then
|
||||||
|
|
||||||
If you're wanting to work on cljs development, you need two terminal sessions. In one run
|
lein repl
|
||||||
|
|
||||||
lein run
|
Wait for the clojure `user=>` prompt to appear, and enter
|
||||||
|
|
||||||
as above; in the other, run
|
(mount/start)
|
||||||
|
|
||||||
|
This will get the application running for development; ideally, open a new terminal and invoke
|
||||||
|
|
||||||
lein figwheel
|
lein figwheel
|
||||||
|
|
||||||
|
which will aid in work on the ClojureScript components.
|
||||||
|
|
||||||
## Running in a production environment
|
## Running in a production environment
|
||||||
|
|
||||||
Doesn't really work yet; if you want to try it, see [Bug #36](https://github.com/simon-brooke/youyesyet/issues/36) and check out the associated feature branch.
|
Doesn't really work yet; if you want to try it, see [Bug #36](https://github.com/simon-brooke/youyesyet/issues/36) and check out the associated feature branch.
|
||||||
|
|
|
@ -53,6 +53,7 @@ FROM followuprequests as request,
|
||||||
where not exists (select * from followupactions as action
|
where not exists (select * from followupactions as action
|
||||||
where action.request_id = request.id
|
where action.request_id = request.id
|
||||||
and action.closed = true)
|
and action.closed = true)
|
||||||
|
and request.locked_by is null
|
||||||
and request.elector_id = electors.id
|
and request.elector_id = electors.id
|
||||||
and request.visit_id = visits.id
|
and request.visit_id = visits.id
|
||||||
and visits.address_id = addresses.id
|
and visits.address_id = addresses.id
|
||||||
|
|
|
@ -30,6 +30,7 @@ SELECT electors.name ||', '|| addresses.address ||', '|| addresses.postcode ||',
|
||||||
followupactions.request_id,
|
followupactions.request_id,
|
||||||
canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS actor_expanded,
|
canvassers.username ||', '|| canvassers.fullname ||', '|| addresses.address ||', '|| addresses.postcode ||', '|| canvassers.phone ||', '|| canvassers.email AS actor_expanded,
|
||||||
followupactions.actor,
|
followupactions.actor,
|
||||||
|
canvassers.fullname AS actor_name,
|
||||||
followupactions.date,
|
followupactions.date,
|
||||||
followupactions.notes,
|
followupactions.notes,
|
||||||
followupactions.closed,
|
followupactions.closed,
|
||||||
|
@ -43,3 +44,10 @@ WHERE followupactions.request_id = followuprequests.id
|
||||||
AND followupactions.actor = canvassers.id
|
AND followupactions.actor = canvassers.id
|
||||||
;
|
;
|
||||||
GRANT SELECT ON lv_followupactions TO canvassers, issueexperts;
|
GRANT SELECT ON lv_followupactions TO canvassers, issueexperts;
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- request locking
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
ALTER TABLE followuprequests ADD COLUMN locked_by INTEGER REFERENCES canvassers(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE followuprequests ADD COLUMN locked TIMESTAMP;
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block extra-head %}
|
||||||
|
<meta http-equiv="refresh" content="60">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block back-links %}
|
{% block back-links %}
|
||||||
<div>
|
<div>
|
||||||
<div class='back-link-container'>
|
<div class='back-link-container'>
|
||||||
|
@ -81,9 +85,13 @@ Method
|
||||||
{{ record.raised }}
|
{{ record.raised }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
{% ifequal record.issue_id "Other" %}
|
||||||
|
{{ record.issue_detail }}
|
||||||
|
{% else %}
|
||||||
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
|
<a href='{{servlet-context}}/form-issues-Issue?id={{ record.issue_id }}'>
|
||||||
{{ record.issue_id_expanded }}
|
{{ record.issue_id }}
|
||||||
</a>
|
</a>
|
||||||
|
{% endifequal %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href='{{servlet-context}}/form-followupmethods-Followupmethod?id={{ record.method_id }}'>
|
<a href='{{servlet-context}}/form-followupmethods-Followupmethod?id={{ record.method_id }}'>
|
||||||
|
|
|
@ -49,12 +49,12 @@
|
||||||
Issue
|
Issue
|
||||||
</label>
|
</label>
|
||||||
{% ifmemberof issueexperts analysts issueeditors admin %}
|
{% ifmemberof issueexperts analysts issueeditors admin %}
|
||||||
<span id='visit' name='visit' class='pseudo-widget disabled'>
|
<span id='issue_id' name='issue_id' class='pseudo-widget disabled'>
|
||||||
{{issue.id}}
|
{{issue.id}} <em>{{record.issue_detail}}</em>
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span id='visit_id' name='visit_id' class='pseudo-widget not-authorised'>
|
<span id='issue_id' name='issue_id' class='pseudo-widget not-authorised'>
|
||||||
You are not permitted to view visit of followuprequests
|
You are not permitted to view issue of followuprequests
|
||||||
</span>
|
</span>
|
||||||
{% endifmemberof %}
|
{% endifmemberof %}
|
||||||
{% ifmemberof issueexperts admin %}
|
{% ifmemberof issueexperts admin %}
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for action in actions %}
|
{% for action in actions %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{action.actor}}</td>
|
<td><a href="{{servlet-context}}/form-canvassers-Canvasser?id={{action.actor}}">{{action.actor_name}}</a></td>
|
||||||
<td>{{action.date}}</td>
|
<td>{{action.date}}</td>
|
||||||
<td>{{action.closed}}</td>
|
<td>{{action.closed}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -116,13 +116,13 @@
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if not closed %}
|
||||||
<p class='widget'>
|
<p class='widget'>
|
||||||
<label for='notes'>
|
<label for='notes'>
|
||||||
Your notes
|
Your notes
|
||||||
</label>
|
</label>
|
||||||
{% ifmemberof admin issueexperts %}
|
{% ifmemberof admin issueexperts %}
|
||||||
<textarea rows='8' cols='60' id='notes' name='notes'>
|
<textarea rows='8' cols='60' id='notes' name='notes'></textarea>
|
||||||
</textarea>
|
|
||||||
{% endifmemberof %}
|
{% endifmemberof %}
|
||||||
</p>
|
</p>
|
||||||
<p class='widget'>
|
<p class='widget'>
|
||||||
|
@ -156,6 +156,7 @@
|
||||||
<input id='save-button' name='save-button' class='action-safe' type='submit' value='Save!'/>
|
<input id='save-button' name='save-button' class='action-safe' type='submit' value='Save!'/>
|
||||||
</p>
|
</p>
|
||||||
{% endifmemberof %}
|
{% endifmemberof %}
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
(:require [adl-support.core :as support]
|
(:require [adl-support.core :as support]
|
||||||
[adl-support.utils :refer [safe-name]]
|
[adl-support.utils :refer [safe-name]]
|
||||||
[clojure.java.io :as io]
|
[clojure.java.io :as io]
|
||||||
|
[clojure.java.jdbc :as jdbc]
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[clojure.walk :refer [keywordize-keys]]
|
[clojure.walk :refer [keywordize-keys]]
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
[noir.util.route :as route]
|
[noir.util.route :as route]
|
||||||
[ring.util.http-response :as response]
|
[ring.util.http-response :as response]
|
||||||
[youyesyet.config :refer [env]]
|
[youyesyet.config :refer [env]]
|
||||||
|
[youyesyet.db.core :refer [*db*]]
|
||||||
[youyesyet.db.core :as db]
|
[youyesyet.db.core :as db]
|
||||||
[youyesyet.layout :as layout]
|
[youyesyet.layout :as layout]
|
||||||
[youyesyet.oauth :as oauth]
|
[youyesyet.oauth :as oauth]
|
||||||
|
@ -51,72 +53,122 @@
|
||||||
(let [user (:user (:session request))]
|
(let [user (:user (:session request))]
|
||||||
{:title "Open requests"
|
{:title "Open requests"
|
||||||
:user user
|
:user user
|
||||||
:records (db/list-open-requests db/*db* {:expert (:id user)})})))
|
:records (db/list-open-requests *db* {:expert (:id user)})})))
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-and-lock-followuprequest!
|
||||||
|
"Return the `followuprequest` record indicated by this `id`, provided that
|
||||||
|
it is unlocked. As a side effect, lock it to this `user`."
|
||||||
|
[id user]
|
||||||
|
(support/do-or-log-error
|
||||||
|
(jdbc/with-db-transaction [*db* *db*]
|
||||||
|
(let [record (db/get-followuprequest *db* {:id id})]
|
||||||
|
(if-not
|
||||||
|
(:locked record)
|
||||||
|
(do
|
||||||
|
(db/update-followuprequest!
|
||||||
|
*db*
|
||||||
|
(assoc
|
||||||
|
record
|
||||||
|
:locked_by (:id user)
|
||||||
|
:locked (jt/to-sql-timestamp (jt/local-date-time))))
|
||||||
|
record))))
|
||||||
|
:error-return nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defn release-followuprequest!
|
||||||
|
"Release the lock held on the `followuprequest` record indicated by this
|
||||||
|
`id` held by this `user`, if present."
|
||||||
|
[id user]
|
||||||
|
(log/debug "release-followuprequest! Attempting to unlock followuprequest " id)
|
||||||
|
(support/do-or-log-error
|
||||||
|
(jdbc/with-db-transaction [*db* *db*]
|
||||||
|
(let [record (db/get-followuprequest *db* {:id id})]
|
||||||
|
(if
|
||||||
|
(= (:locked_by record) (:id user))
|
||||||
|
(do
|
||||||
|
(db/update-followuprequest!
|
||||||
|
*db*
|
||||||
|
(assoc
|
||||||
|
record
|
||||||
|
:locked_by nil
|
||||||
|
:locked nil))
|
||||||
|
true))))
|
||||||
|
:error-return nil))
|
||||||
|
|
||||||
|
|
||||||
(defn get-followup-request-page [request]
|
(defn get-followup-request-page [request]
|
||||||
(let
|
(let
|
||||||
[params (support/massage-params request)
|
[user (:user (:session request))
|
||||||
|
params (support/massage-params request)
|
||||||
id (:id params)
|
id (:id params)
|
||||||
record (db/get-followuprequest db/*db* {:id id})
|
record (get-and-lock-followuprequest! id user)
|
||||||
elector (if
|
elector (if
|
||||||
record
|
record
|
||||||
(first
|
(first
|
||||||
(db/search-strings-electors
|
(db/search-strings-electors
|
||||||
db/*db* {:id (:elector_id record)})))
|
*db* {:id (:elector_id record)})))
|
||||||
visit (if
|
visit (if
|
||||||
record
|
record
|
||||||
(first
|
(first
|
||||||
(db/search-strings-visits
|
(db/search-strings-visits
|
||||||
db/*db* {:id (:visit_id record)})))]
|
*db* {:id (:visit_id record)})))
|
||||||
|
actions (db/list-followupactions-by-followuprequest
|
||||||
|
*db* {:id id})]
|
||||||
|
(if record
|
||||||
(layout/render
|
(layout/render
|
||||||
"issue-expert/request.html"
|
"issue-expert/request.html"
|
||||||
{:actions (map
|
{:actions (map
|
||||||
;; HTML-ise the notes in each action record
|
;; HTML-ise the notes in each action record
|
||||||
#(merge % {:notes (md-to-html-string (:notes %))})
|
#(merge % {:notes (md-to-html-string (:notes %))})
|
||||||
(db/list-followupactions-by-followuprequest
|
actions)
|
||||||
db/*db* {:id id}))
|
|
||||||
:elector elector
|
:elector elector
|
||||||
:issue (let
|
:issue (let
|
||||||
[raw-issue (if
|
[raw-issue (if
|
||||||
record
|
record
|
||||||
(db/get-issue db/*db* {:id (:issue_id record)}))]
|
(db/get-issue *db* {:id (:issue_id record)}))]
|
||||||
(if raw-issue
|
(if raw-issue
|
||||||
(merge
|
(merge
|
||||||
raw-issue
|
raw-issue
|
||||||
{:brief (md-to-html-string (:brief raw-issue))})))
|
{:brief (md-to-html-string (:brief raw-issue))})))
|
||||||
:options (db/list-options db/*db* params)
|
:options (db/list-options *db* params)
|
||||||
:record record
|
:record record
|
||||||
:title (str "Request from " (:name elector) " at " (:date visit))
|
:title (str "Request from " (:name elector) " at " (:date visit))
|
||||||
:user (:user (:session request))
|
:user (:user (:session request))
|
||||||
:visit visit})))
|
:visit visit
|
||||||
|
:closed (some :closed actions)})
|
||||||
|
(list-page (assoc request :error "That request is locked")))))
|
||||||
|
|
||||||
|
|
||||||
(defn post-followup-action
|
(defn post-followup-action
|
||||||
"From this `request`, create a `followupaction` record, and, if an
|
"From this `request`, create a `followupaction` record, and, if an
|
||||||
`option_id` is present in the params, an `intention` record; show
|
`option_id` is present in the params, an `intention` record; show
|
||||||
the request list on success, to the request form on failure."
|
the request list on success, the request form on failure."
|
||||||
[request]
|
[request]
|
||||||
(support/do-or-log-error
|
(support/do-or-log-error
|
||||||
(let
|
(let
|
||||||
[params (support/massage-params request)
|
[user (:user (:session request))
|
||||||
locality (:locality (db/get-locality-for-visit db/*db* {:id (:visit_id params)}))]
|
params (support/massage-params request)
|
||||||
|
locality (:locality (db/get-locality-for-visit *db* {:id (:visit_id params)}))]
|
||||||
(log/debug "post-followup-request-page with request " request)
|
(log/debug "post-followup-request-page with request " request)
|
||||||
|
(support/do-or-log-error
|
||||||
|
(jdbc/with-db-transaction [*db* *db*]
|
||||||
(db/create-followupaction!
|
(db/create-followupaction!
|
||||||
db/*db*
|
*db*
|
||||||
(assoc
|
(assoc
|
||||||
params
|
params
|
||||||
:actor (:id (:user (:session request)))
|
:actor (:id user)
|
||||||
:date (jt/to-sql-timestamp (jt/local-date-time))
|
:date (jt/to-sql-timestamp (jt/local-date-time))
|
||||||
:closed (= (:closed params) "on")))
|
:closed (= (:closed params) "on")))
|
||||||
|
(release-followuprequest! (:id params) user)))
|
||||||
(if-not
|
(if-not
|
||||||
(zero? (count (:option_id params)))
|
(zero? (count (:option_id params)))
|
||||||
(if
|
(if
|
||||||
(zero? (count (:signature (db/get-elector db/*db* {:id (:elector_id params)}))))
|
(zero? (count (:signature (db/get-elector *db* {:id (:elector_id params)}))))
|
||||||
;; the elector has NOT recorded GDPR consent: explicitly bind elector_id to nil
|
;; the elector has NOT recorded GDPR consent: explicitly bind elector_id to nil
|
||||||
(db/create-intention! db/*db* (assoc params :locality locality :elector_id nil))
|
(db/create-intention! *db* (assoc params :locality locality :elector_id nil))
|
||||||
;; else the elector HAS recorded GDPR consent
|
;; else the elector HAS recorded GDPR consent
|
||||||
(db/create-intention! db/*db* (assoc params :locality locality))))
|
(db/create-intention! *db* (assoc params :locality locality))))
|
||||||
(list-page request))
|
(list-page request))
|
||||||
:error-return
|
:error-return
|
||||||
(get-followup-request-page request)))
|
(get-followup-request-page request)))
|
||||||
|
|
|
@ -157,7 +157,7 @@
|
||||||
without having recorded the visit, so let's not muck about."
|
without having recorded the visit, so let's not muck about."
|
||||||
[request]
|
[request]
|
||||||
(let [params (merge
|
(let [params (merge
|
||||||
{:actions nil, :issue_detail ""}
|
{:actions nil, :issue_detail nil :locked_by nil :locked nil}
|
||||||
(assoc
|
(assoc
|
||||||
(massage-params request)
|
(massage-params request)
|
||||||
:visit_id (current-visit-id request)))]
|
:visit_id (current-visit-id request)))]
|
||||||
|
|
|
@ -344,7 +344,8 @@
|
||||||
{:address_id (-> db :address :id)
|
{:address_id (-> db :address :id)
|
||||||
:elector_id (-> db :elector :id)
|
:elector_id (-> db :elector :id)
|
||||||
:issue_id (name (-> db :issue))
|
:issue_id (name (-> db :issue))
|
||||||
:method_id "Phone"
|
:issue_detail (-> db :issue-detail)
|
||||||
|
:method_id (-> db :followupmethod)
|
||||||
:method_detail (-> db :method_detail)
|
:method_detail (-> db :method_detail)
|
||||||
:action :create-request})
|
:action :create-request})
|
||||||
:send-request))
|
:send-request))
|
||||||
|
@ -468,6 +469,12 @@
|
||||||
(js/console.log (str "Setting issue to " issue))
|
(js/console.log (str "Setting issue to " issue))
|
||||||
(assoc (clear-messages db) :issue (keyword issue))))
|
(assoc (clear-messages db) :issue (keyword issue))))
|
||||||
|
|
||||||
|
(reg-event-db
|
||||||
|
:set-issue-detail
|
||||||
|
(fn [db [_ issue-detail]]
|
||||||
|
(js/console.log (str "Setting issue-detail to " issue-detail))
|
||||||
|
(assoc (clear-messages db) :issue-detail issue-detail)))
|
||||||
|
|
||||||
|
|
||||||
(reg-event-db
|
(reg-event-db
|
||||||
:set-latitude
|
:set-latitude
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
dwelling @(subscribe [:dwelling])
|
dwelling @(subscribe [:dwelling])
|
||||||
method @(subscribe [:followupmethod])]
|
method @(subscribe [:followupmethod])]
|
||||||
(js/console.log (str "followup/panel; Issue is " issue "; elector is " elector "; method is " method " (" (type method) ")"))
|
(js/console.log (str "followup/panel; Issue is " issue "; elector is " elector "; method is " method " (" (type method) ")"))
|
||||||
|
(dispatch [:set-followupmethod "Phone"])
|
||||||
|
(dispatch [:set-method-detail nil])
|
||||||
|
(dispatch [:set-issue-detail nil])
|
||||||
(cond
|
(cond
|
||||||
(nil? dwelling)
|
(nil? dwelling)
|
||||||
(ui/error-panel "No dwelling selected")
|
(ui/error-panel "No dwelling selected")
|
||||||
|
@ -72,7 +75,8 @@
|
||||||
(if (= issue :Other)
|
(if (= issue :Other)
|
||||||
[:p.widget
|
[:p.widget
|
||||||
[:label {:for "issue_detail"} "Issue detail"]
|
[:label {:for "issue_detail"} "Issue detail"]
|
||||||
[:input {:type "text" :id "issue_detail" :name "issue_detail"}]])
|
[:input {:type "text" :id "issue_detail" :name "issue_detail"
|
||||||
|
:on-change #(dispatch [:set-issue-detail (.-value (.-target %))])}]])
|
||||||
[:p.widget
|
[:p.widget
|
||||||
[:label {:for "method"} "Method"]
|
[:label {:for "method"} "Method"]
|
||||||
[:select {:id "method" :name "method" :defaultValue "Phone"
|
[:select {:id "method" :name "method" :defaultValue "Phone"
|
||||||
|
|
|
@ -703,11 +703,16 @@ version="0.1.1">
|
||||||
column="method_id" entity="followupmethods" farkey="id">
|
column="method_id" entity="followupmethods" farkey="id">
|
||||||
<prompt prompt="method_id" locale="en_GB.UTF-8"/>
|
<prompt prompt="method_id" locale="en_GB.UTF-8"/>
|
||||||
</property>
|
</property>
|
||||||
<property required="true" type="string" name="method-detail" size="128">
|
<property required="true" type="string" name="method_detail" column="method_detail" size="128">
|
||||||
<documentation>
|
<documentation>
|
||||||
Phone number or email address for followup.
|
Phone number or email address for followup.
|
||||||
</documentation>
|
</documentation>
|
||||||
</property>
|
</property>
|
||||||
|
<property type='entity' entity='canvassers' name='locked_by' column='locked_by'>
|
||||||
|
<documentation>The issue expert who is currently handling this issue, if any.</documentation>
|
||||||
|
<prompt prompt="Locked by" locale="en_GB.UTF-8"/>
|
||||||
|
</property>
|
||||||
|
<property type="timestamp" name="locked"/>
|
||||||
<property type="list" name="actions" entity="followupactions" farkey="request_id"/>
|
<property type="list" name="actions" entity="followupactions" farkey="request_id"/>
|
||||||
<list properties="listed" name="Followuprequests">
|
<list properties="listed" name="Followuprequests">
|
||||||
<field property="elector_id">
|
<field property="elector_id">
|
||||||
|
|
Loading…
Reference in a new issue