new holds pull list interface new-hold-pull-list
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Mon, 2 Apr 2012 23:13:01 +0000 (19:13 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Mon, 2 Apr 2012 23:13:01 +0000 (19:13 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/templates/circ/hold_pull_list.tt2
Open-ILS/web/js/dojo/openils/FlattenerStore.js
Open-ILS/web/js/dojo/openils/widget/FlattenerGrid.js
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/server/patron/holds.js
Open-ILS/xul/staff_client/server/patron/holds_overlay.xul

index 2498393..5b200e3 100644 (file)
@@ -4830,11 +4830,26 @@ SELECT  usr,
                <oils_persist:source_definition><![CDATA[
                SELECT
                        ahr.*,
-                       COALESCE(999, acplo.position) AS copy_location_order_position
+                       COALESCE(acplo.position, 999) AS
+                               copy_location_order_position,
+                       CASE WHEN au.alias IS NOT NULL THEN
+                               au.alias
+                       ELSE
+                               REGEXP_REPLACE(ARRAY_TO_STRING(ARRAY[
+                                       COALESCE(au.family_name, ''),
+                                       COALESCE(au.suffix, ''),
+                                       ', ',
+                                       COALESCE(au.prefix, ''),
+                                       COALESCE(au.first_given_name, ''),
+                                       COALESCE(au.second_given_name, '')
+                               ], ' '), E'\\s+,', ',')
+                       END AS usr_display_name
                FROM action.hold_request ahr
                JOIN asset.copy acp ON (acp.id = ahr.current_copy)
+               JOIN actor.usr au ON (au.id = ahr.usr)
                LEFT JOIN asset.copy_location_order acplo
-                       ON (acp.location = acplo.location AND acp.circ_lib = acplo.org)
+                       ON (acp.location = acplo.location AND
+                               acp.circ_lib = acplo.org)
                WHERE
                        ahr.capture_time IS NULL AND
                        ahr.cancel_time IS NULL AND
@@ -4882,6 +4897,7 @@ SELECT  usr,
                        <field reporter:label="Notes" name="notes" reporter:datatype="link" oils_persist:virtual="true"/>
                        <field reporter:label="Current Shelf Lib" name="current_shelf_lib" reporter:datatype="org_unit"/>
                        <field reporter:label="Copy Location Sort Order" name="copy_location_order_position" reporter:datatype="int" />
+                       <field reporter:label="User Display Name" name="usr_display_name" reporter:datatype="text" />
                </fields>
                <links>
                        <link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
index e64a12d..03d3987 100644 (file)
@@ -77,7 +77,7 @@
         <div>Hold Pull List</div>
         <div>
             <button dojoType="dijit.form.Button"
-                onClick="print_pull_list()">Print Pull List</button>
+                onClick="grid.print();">Print Pull List</button>
         </div>
     </div>
     <div class="oils-acq-basic-roomy">
         <select
             id="org_selector" jsId="org_selector"
             dojoType="openils.widget.OrgUnitFilteringSelect"
-            searchAttr="shortname" labelAttr="shortname">
+            searchAttr="name" labelAttr="name">
         </select>
     </div>
     <table
-        id="gridNode"
         jsid="grid"
         dojoType="openils.widget.FlattenerGrid"
         columnPersistKey='"circ.hold_pull_list"'
         autoHeight="10"
-        editOnEnter="true"
+        editOnEnter="false"
+        hideSelector="true"
         editStyle="pane"
         showLoadFilter="true"
         fmClass="'ahopl'"
         query="{}">
         <thead>
             <tr>
-                <th field="barcode" fpath="current_copy.barcode">Barcode</th>
+                <th field="barcode" fpath="current_copy.barcode"></th>
                 <th field="title" fpath="current_copy.call_number.record.simple_record.title"></th>
                 <th field="author" fpath="current_copy.call_number.record.simple_record.author"></th>
-                <th field="call_number" fpath="current_copy.call_number.label" ffilter="true" get="get_cn_extras" formatter="format_cn_extras"></th>
+                <th field="call_number" fpath="current_copy.call_number.label" get="get_cn_extras" formatter="format_cn_extras"></th>
                 <th field="shelving_loc" fpath="current_copy.location.name" ffilter="true">Shelving Location</th>
+                <th field="usr_display_name" fpath="usr_display_name">Patron Name or Alias</th>
+                <th field="request_time" fpath="request_time"></th>
+                <th field="expire_time" fpath="expire_time"></th>
             </tr>
         </thead>
     </table>
index c3af728..6ffab89 100644 (file)
@@ -78,7 +78,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             }
         },
 
-        "_prepare_flattener_params": function(req) {
+        "_build_flattener_params": function(req) {
             var params = {
                 "hint": this.fmClass,
                 "ses": openils.User.authtoken
@@ -92,26 +92,26 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
 
                 params.where = dojo.toJson(where);
             } else {
-                var limit = (!isNaN(req.count) && req.count != Infinity) ?
-                    req.count : this.limit;
-                var offset = (!isNaN(req.start) && req.start != Infinity) ?
-                    req.start : this.offset;
-
-                dojo.mixin(
-                    params, {
-                        "where": dojo.toJson(req.query),
-                        "slo": dojo.toJson({
-                            "sort": this._remap_sort(
-                                this._prepare_sort(req.sort)
-                            ),
-                            "limit": limit,
-                            "offset": offset
-                        })
-                    }
-                );
+                params.where =  dojo.toJson(req.query);
+
+                var slo = {
+                    "sort": this._remap_sort(this._prepare_sort(req.sort))
+                };
+
+                if (!req.queryOptions.all) {
+                    slo.limit =
+                        (!isNaN(req.count) && req.count != Infinity) ?
+                            req.count : this.limit;
+
+                    slo.offset =
+                        (!isNaN(req.start) && req.start != Infinity) ?
+                            req.start : this.offset;
+                }
+
+                params.slo = dojo.toJson(slo);
             }
 
-            if (this.mapKey) { /* XXX TODO, get a map key */
+            if (this.mapKey) {
                 params.key = this.mapKey;
             } else {
                 params.map = dojo.toJson(this.mapClause);
@@ -143,6 +143,94 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             );
         },
 
+        "_on_http_error": function(response, ioArgs, req, retry_method) {
+            if (response.status == 402) {   /* 'Payment Required' stands
+                                               in for cache miss */
+                if (this._retried_map_key_already) {
+                    var e = new FlattenerStoreError(
+                        "Server won't cache flattener map?"
+                    );
+                    if (typeof req.onError == "function")
+                        req.onError.call(callback_scope, e);
+                    else
+                        throw e;
+                } else {
+                    this._retried_map_key_already = true;
+                    delete this.mapKey;
+                    if (retry_method)
+                        return this[retry_method](req);
+                }
+            }
+        },
+
+        "_fetch_prepare": function(req) {
+            req.queryOptions = req.queryOptions || {};
+            req.abort = function() { console.warn("[unimplemented] abort()"); };
+
+            if (!this.mapKey)
+                this._get_map_key();
+
+            return this._build_flattener_params(req);
+        },
+
+        "_fetch_execute": function(params,handle_as,mime_type,onload,onerror) {
+            dojo.xhrPost({
+                "url": this._flattener_url,
+                "content": params,
+                "handleAs": handle_as,
+                "sync": false,
+                "preventCache": true,
+                "headers": {"Accept": mime_type},
+                "load": onload,
+                "error": onerror
+            });
+        },
+
+        /* *** Nonstandard but public API - Please think hard about doing
+         * things the Dojo Way whenever possible before extending the API
+         * here. *** */
+
+        /* fetchToPrint() acts like a lot like fetch(), but doesn't call
+         * onBegin or onComplete.  */
+        "fetchToPrint": function(req) {
+            var callback_scope = req.scope || dojo.global;
+            var post_params;
+
+            try {
+                post_params = this._fetch_prepare(req);
+            } catch (E) {
+                if (typeof req.onError == "function")
+                    req.onError.call(callback_scope, E);
+                else
+                    throw E;
+            }
+
+            var process_fetch_all = dojo.hitch(
+                this, function(text) {
+                    this._retried_map_key_already = false;
+
+                    if (typeof req.onComplete == "function")
+                        req.onComplete.call(callback_scope, text, req);
+                }
+            );
+
+            var process_error = dojo.hitch(
+                this, function(response, ioArgs) {
+                    this._on_http_error(response, ioArgs, req, "fetchToPrint");
+                }
+            );
+
+            this._fetch_execute(
+                post_params,
+                "text",
+                "text/html",
+                process_fetch_all,
+                process_error
+            );
+
+            return req;
+        },
+
         /* *** Begin dojo.data.api.Read methods *** */
 
         "getValue": function(
@@ -252,35 +340,18 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             //      onItem   a callback that takes each item as we get it
             //      onComplete  a callback that takes the list of items
             //                      after they're all fetched
-            //
-            //  The onError callback is ignored for now (haven't thought
-            //  of anything useful to do with it yet).
-            //
-            //  The Read API also charges this method with adding an abort
-            //  callback to the *req* object for the caller's use, but
-            //  the one we provide does nothing but issue an alert().
 
-            //console.log("fetch(" + dojo.toJson(req) + ")");
             var self = this;
             var callback_scope = req.scope || dojo.global;
-
-            if (!this.mapKey) {
-                try {
-                    this._get_map_key();
-                } catch (E) {
-                    if (req.onError)
-                        req.onError.call(callback_scope, E);
-                    else
-                        throw E;
-                }
-            }
-
-            var post_params = this._prepare_flattener_params(req);
-
-            if (!post_params) {
-                if (typeof req.onComplete == "function")
-                    req.onComplete.call(callback_scope, [], req);
-                return;
+            var post_params;
+
+            try {
+                post_params = this._fetch_prepare(req);
+            } catch (E) {
+                if (typeof req.onError == "function")
+                    req.onError.call(callback_scope, E);
+                else
+                    throw E;
             }
 
             var process_fetch = function(obj, when) {
@@ -325,41 +396,21 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                     req.onComplete.call(callback_scope, obj, req);
             };
 
-            req.abort = function() {
-                throw new FlattenerStoreError(
-                    "The 'abort' operation is not supported"
-                );
-            };
+            var process_error = dojo.hitch(
+                this, function(response, ioArgs) {
+                    this._on_http_error(response, ioArgs, req, "fetch");
+                }
+            );
 
             var fetch_time = this._last_fetch = (new Date().getTime());
 
-            dojo.xhrPost({
-                "url": this._flattener_url,
-                "content": post_params,
-                "handleAs": "json",
-                "sync": false,
-                "preventCache": true,
-                "headers": {"Accept": "application/json"},
-                "load": function(obj) { process_fetch(obj, fetch_time); },
-                "error": function(response, ioArgs) {
-                    if (response.status == 402) {   /* 'Payment Required' stands
-                                                       in for cache miss */
-                        if (self._retried_map_key_already) {
-                            var e = new FlattenerStoreError(
-                                "Server won't cache flattener map?"
-                            );
-                            if (typeof req.onError == "function")
-                                req.onError.call(callback_scope, e);
-                            else
-                                throw e;
-                        } else {
-                            self._retried_map_key_already = true;
-                            delete self.mapKey;
-                            return self.fetch(req);
-                        }
-                    }
-                }
-            });
+            this._fetch_execute(
+                post_params,
+                "json",
+                "application/json",
+                function(obj) { process_fetch(obj, fetch_time); },
+                process_error
+            );
 
             return req;
         },
@@ -397,7 +448,15 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 return;
             }
 
-            var post_params = this._prepare_flattener_params(keywordArgs);
+            var post_params;
+            try {
+                post_params = this._fetch_prepare(keywordArgs);
+            } catch (E) {
+                if (typeof keywordArgs.onError == "function")
+                    keywordArgs.onError.call(callback_scope, E);
+                else
+                    throw E;
+            }
 
             var process_fetch_one = dojo.hitch(
                 this, function(obj, when) {
@@ -433,17 +492,23 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
                 }
             );
 
+            var process_error = dojo.hitch(
+                this, function(response, ioArgs) {
+                    this._on_http_error(
+                        response, ioArgs, keywordArgs, "fetchItemByIdentity"
+                    );
+                }
+            );
+
             var fetch_time = this._last_fetch = (new Date().getTime());
 
-            dojo.xhrPost({
-                "url": this._flattener_url,
-                "content": post_params,
-                "handleAs": "json",
-                "sync": false,
-                "preventCache": true,
-                "headers": {"Accept": "application/json"},
-                "load": function(obj){ process_fetch_one(obj, fetch_time); }
-            });
+            this._fetch_execute(
+                post_params,
+                "json",
+                "application/json",
+                function(obj) { process_fetch_one(obj, fetch_time); },
+                process_error
+            );
         },
 
         /* dojo.data.api.Write - only very partially implemented, because
index f7e7832..809f4f9 100644 (file)
@@ -688,6 +688,21 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                         );
                     }
                 );
+            },
+
+            /* Print the same data that the Flattener is feeding to the
+             * grid, sorted the same way too. remove limit and offset (i.e.,
+             * print it all. */
+            "print": function() {
+                var req = {
+                    "query": this.query,
+                    "queryOptions": {"all": true},
+                    "onComplete": function(text) {
+                        openils.Util.printHtmlString(text);
+                    }
+                };
+
+                this.store.fetchToPrint(req);
             }
         }
     );
index 29e809c..83f875a 100644 (file)
 <!ENTITY staff.patron.holds_overlay.print.accesskey "P">
 <!ENTITY staff.patron.holds_overlay.print_full_pull_list.label "Print Full Pull List">
 <!ENTITY staff.patron.holds_overlay.print_full_pull_list.accesskey "u">
-<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.label "Print Full Pull List (Alternate strategy)">
-<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.accesskey "y">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.label "Simplified Pull List Interface">
+<!ENTITY staff.patron.holds_overlay.print_alt_pull_list.accesskey "i">
 <!ENTITY staff.patron.holds_overlay.place_hold.label "Place Hold">
 <!ENTITY staff.patron.holds_overlay.place_hold.accesskey "H">
 <!ENTITY staff.patron.holds_overlay.show_cancelled_holds.label "Show Cancelled Holds">
index cb45955..927aa6d 100644 (file)
@@ -352,7 +352,7 @@ patron.holds.prototype = {
                             }
                         }
                     ],
-                    'cmd_holds_print_alt' : [
+                    'cmd_simplified_pull_list' : [
                         ['command'],
                         function() {
                             try {
@@ -376,12 +376,13 @@ patron.holds.prototype = {
                                 });
 
                                 var loc = urls.XUL_BROWSER + "?url=" + window.escape(
-                                    xulG.url_prefix("/opac/extras/circ/alt_holds_print.html").replace("http:","https:")
+                                    xulG.url_prefix("/eg/circ/hold_pull_list").replace("http:","https:")
                                 );
                                 xulG.new_tab(
                                     loc, {
-                                        "tab_name": "Printable Pull List", /* XXX i18n */
-                                        "browser": false
+                                        "tab_name": "Simplified Pull List", /* XXX i18n */
+                                        "browser": false,
+                                        "show_print_button": false
                                     }, content_params
                                 );
                             } catch (E) {
@@ -1488,7 +1489,7 @@ patron.holds.prototype = {
         var x_clear_shelf_widgets = document.getElementById('clear_shelf_widgets');
         var x_expired_checkbox = document.getElementById('expired_checkbox');
         var x_print_full_pull_list = document.getElementById('print_full_btn');
-        var x_print_full_pull_list_alt = document.getElementById('print_alt_btn');
+        var x_simplified_pull_list = document.getElementById('simplified_pull_list_btn');
         switch(obj.hold_interface_type) {
             case 'shelf':
                 obj.render_lib_menus({'pickup_lib':true});
@@ -1496,12 +1497,12 @@ patron.holds.prototype = {
                 if (x_lib_type_menu) x_lib_type_menu.hidden = false;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
                 if (x_clear_shelf_widgets) x_clear_shelf_widgets.hidden = false;
-                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
+                if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
             break;
             case 'pull' :
                 if (x_fetch_more) x_fetch_more.hidden = false;
                 if (x_print_full_pull_list) x_print_full_pull_list.hidden = false;
-                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = false;
+                if (x_simplified_pull_list) x_simplified_pull_list.hidden = false;
                 if (x_lib_type_menu) x_lib_type_menu.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
             break;
@@ -1509,7 +1510,7 @@ patron.holds.prototype = {
                 obj.render_lib_menus({'pickup_lib':true,'request_lib':true});
                 if (x_lib_filter_checkbox) x_lib_filter_checkbox.hidden = false;
                 if (x_lib_type_menu) x_lib_type_menu.hidden = false;
-                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
+                if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = false;
             break;
             default:
@@ -1518,7 +1519,7 @@ patron.holds.prototype = {
                 if (x_lib_type_menu) x_lib_type_menu.hidden = true;
                 if (x_lib_menu_placeholder) x_lib_menu_placeholder.hidden = true;
                 if (x_show_cancelled_deck) x_show_cancelled_deck.hidden = false;
-                if (x_print_full_pull_list_alt) x_print_full_pull_list_alt.hidden = true;
+                if (x_simplified_pull_list) x_simplified_pull_list.hidden = true;
             break;
         }
         setTimeout( // We do this because render_lib_menus above creates and appends a DOM node, but until this thread exits, it doesn't really happen
index 1e05002..ff8d1d6 100644 (file)
@@ -19,7 +19,7 @@
         <command id="cmd_csv_to_file" />
         <command id="cmd_holds_print" />
         <command id="cmd_holds_print_full" />
-        <command id="cmd_holds_print_alt" />
+        <command id="cmd_simplified_pull_list" />
         <command id="cmd_show_catalog" />
         <command id="cmd_retrieve_patron" />
         <command id="cmd_holds_edit_desire_mint_condition" />
 
         <button id="holds_print" label="&staff.patron.holds_overlay.print.label;" command="cmd_holds_print" accesskey="&staff.patron.holds_overlay.print.accesskey;" />
         <button id="print_full_btn" hidden="true" label="&staff.patron.holds_overlay.print_full_pull_list.label;" command="cmd_holds_print_full" accesskey="&staff.patron.holds_overlay.print_full_pull_list.accesskey;" />
-        <button id="print_alt_btn" hidden="true" label="&staff.patron.holds_overlay.print_alt_pull_list.label;" command="cmd_holds_print_alt" accesskey="&staff.patron.holds_overlay.print_alt_pull_list.accesskey;" />
+        <button id="simplified_pull_list_btn" hidden="true" label="&staff.patron.holds_overlay.print_alt_pull_list.label;" command="cmd_simplified_pull_list" accesskey="&staff.patron.holds_overlay.print_alt_pull_list.accesskey;" />
         <spacer flex="1"/>
     </hbox>