New pull list interface taking advantage of flattener for speed, collab/senator/simplified-hold-pull-list
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Sat, 31 Mar 2012 16:17:40 +0000 (12:17 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 10 Apr 2012 20:08:40 +0000 (16:08 -0400)
and advanced sorting.  For now, access it by the "Simplifed Pull List"
button along the bottom edge of the existing holds pull list interface
(but I think when/if this thing is widely accepted, it should replace
the existing interface outright).

Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/WWW/FlatFielder.pm
Open-ILS/src/templates/circ/hold_pull_list.tt2 [new file with mode: 0644]
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 375f93f..0c3bbbc 100644 (file)
@@ -4766,7 +4766,7 @@ SELECT  usr,
             </actions>
         </permacrud>
        </class>
-       <!-- A note: Please update alhr when updating ahr -->
+       <!-- A note: Please update alhr and ahopl when updating ahr -->
        <class id="ahr" controller="open-ils.cstore" oils_obj:fieldmapper="action::hold_request" oils_persist:tablename="action.hold_request" reporter:core="true" reporter:label="Hold Request">
                <fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_id_seq">
                        <field reporter:label="Status" name="status" oils_persist:virtual="true" />
@@ -4829,6 +4829,109 @@ SELECT  usr,
                        <link field="sms_carrier" reltype="might_have" key="code" map="" class="csc"/>
                </links>
        </class>
+       <class id="ahopl" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::hold_on_pull_list" reporter:label="Hold On Pull List" oils_persist:readonly="true">
+               <oils_persist:source_definition><![CDATA[
+               SELECT
+                       ahr.*,
+                       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,
+                       TRIM(acnp.label || ' ' || acn.label || ' ' || acns.label)
+                               AS call_number_label
+               FROM action.hold_request ahr
+               JOIN asset.copy acp ON (acp.id = ahr.current_copy)
+               JOIN asset.call_number acn ON (acp.call_number = acn.id)
+               JOIN asset.call_number_prefix acnp ON (acn.prefix = acnp.id)
+               JOIN asset.call_number_suffix acns ON (acn.suffix = acns.id)
+               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)
+               WHERE
+                       ahr.capture_time IS NULL AND
+                       ahr.cancel_time IS NULL AND
+                       (ahr.expire_time is NULL OR ahr.expire_time > NOW())
+               ]]></oils_persist:source_definition>
+               <fields oils_persist:primary="id">
+                       <field reporter:label="Status" name="status" oils_persist:virtual="true" />
+                       <field reporter:label="Transit" name="transit" oils_persist:virtual="true" />
+                       <field reporter:label="Capture Date/Time" name="capture_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Currently Targeted Copy" name="current_copy" />
+                       <field reporter:label="Notify by Email?" name="email_notify" reporter:datatype="bool"/>
+                       <field reporter:label="Hold Expire Date/Time" name="expire_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Fulfilling Library" name="fulfillment_lib" reporter:datatype="org_unit"/>
+                       <field reporter:label="Fulfilling Staff" name="fulfillment_staff" />
+                       <field reporter:label="Fulfillment Date/Time" name="fulfillment_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Hold Type" name="hold_type" reporter:datatype="text"/>
+                       <field reporter:label="Holdable Formats (for M-type hold)" name="holdable_formats" reporter:datatype="text"/>
+                       <field reporter:label="Hold ID" name="id" reporter:datatype="id" />
+                       <field reporter:label="Notifications Phone Number" name="phone_notify" reporter:datatype="text"/>
+                       <field reporter:label="Notifications SMS Number" name="sms_notify" reporter:datatype="text"/>
+                       <field reporter:label="Notifications SMS Carrier" name="sms_carrier" reporter:datatype="text"/>
+                       <field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="org_unit"/>
+                       <field reporter:label="Last Targeting Date/Time" name="prev_check_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
+                       <field reporter:label="Request Date/Time" name="request_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Requesting User" name="requestor" reporter:datatype="link"/>
+                       <field reporter:label="Item Selection Depth" name="selection_depth" />
+                       <field reporter:label="Selection Locus" name="selection_ou" reporter:datatype="org_unit"/>
+                       <field reporter:label="Target Object ID" name="target" reporter:datatype="link"/>
+                       <field reporter:label="Hold User" name="usr" reporter:datatype="link"/>
+                       <field reporter:label="Hold Cancel Date/Time" name="cancel_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Notify Time" name="notify_time" oils_persist:virtual="true" reporter:datatype="timestamp"/>
+                       <field reporter:label="Notify Count" name="notify_count" oils_persist:virtual="true" reporter:datatype="int" />
+                       <field reporter:label="Notifications" name="notifications" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Bib Record link" name="bib_rec" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Eligible Copies" name="eligible_copies" oils_persist:virtual="true" reporter:datatype="link"/>
+                       <field reporter:label="Currently Frozen" name="frozen" reporter:datatype="bool"/>
+                       <field reporter:label="Thaw Date (if frozen)" name="thaw_date" reporter:datatype="timestamp"/>
+                       <field reporter:label="Shelf Time" name="shelf_time" reporter:datatype="timestamp"/>
+                       <field reporter:label="Cancelation cause" name="cancel_cause" reporter:datatype="link" />
+                       <field reporter:label="Cancelation note" name="cancel_note" reporter:datatype="text" />
+                       <field reporter:label="Top of Queue" name="cut_in_line" reporter:datatype="bool" />
+                       <field reporter:label="Is Mint Condition" name="mint_condition" reporter:datatype="bool" />
+                       <field reporter:label="Shelf Expire Time" name="shelf_expire_time" reporter:datatype="timestamp"/>
+                       <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" />
+                       <field reporter:label="Call Number Label" name="call_number_label" reporter:datatype="text" />
+               </fields>
+               <links>
+                       <link field="fulfillment_lib" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="fulfillment_staff" reltype="has_a" key="id" map="" class="au"/>
+                       <link field="pickup_lib" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="selection_ou" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="requestor" reltype="has_a" key="id" map="" class="au"/>
+                       <link field="current_copy" reltype="has_a" key="id" map="" class="acp"/>
+                       <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+                       <link field="request_lib" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="transit" reltype="might_have" key="hold" map="" class="ahtc"/>
+                       <link field="notifications" reltype="has_many" key="hold" map="" class="ahn"/>
+                       <link field="eligible_copies" reltype="has_many" key="hold" map="target_copy" class="ahcm"/>
+                       <link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
+                       <link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
+                       <link field="notes" reltype="has_many" key="hold" map="" class="ahrn"/>
+                       <link field="current_shelf_lib" reltype="has_a" key="id" map="" class="aou"/>
+                       <link field="sms_carrier" reltype="might_have" key="code" map="" class="csc"/>
+               </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <retrieve permission="VIEW_HOLD" context_field="pickup_lib" />
+                       </actions>
+               </permacrud>
+       </class>
        <class id="alhr" controller="open-ils.cstore" oils_obj:fieldmapper="action::last_hold_request" reporter:label="Last Captured Hold Request" oils_persist:readonly="true">
                <oils_persist:source_definition>
                        SELECT ahr.* FROM action.hold_request ahr JOIN (SELECT current_copy, MAX(capture_time) AS capture_time FROM action.hold_request WHERE capture_time IS NOT NULL GROUP BY current_copy)x USING (current_copy, capture_time)
@@ -7831,7 +7934,7 @@ SELECT  usr,
                        <link field="folder" reltype="has_a" key="id" map="" class="rof"/>
                </links>
        </class>
-       <class id="rmsr" controller="open-ils.reporter-store open-ils.cstore" oils_obj:fieldmapper="reporter::materialized_simple_record" oils_persist:tablename="reporter.materialized_simple_record" reporter:label="Fast Simple Record Extracts">
+       <class id="rmsr" controller="open-ils.reporter-store open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="reporter::materialized_simple_record" oils_persist:tablename="reporter.materialized_simple_record" reporter:label="Fast Simple Record Extracts">
                <fields oils_persist:primary="id">
                        <field reporter:label="Record ID" name="id" reporter:datatype="id" />
                        <field reporter:label="Fingerprint" name="fingerprint" reporter:datatype="text"/>
@@ -7849,6 +7952,11 @@ SELECT  usr,
                <links>
                        <link field="biblio_record" reltype="might_have" key="id" map="" class="bre"/>
                </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <retrieve />
+                       </actions>
+               </permacrud>
        </class>
        <class id="rssr" controller="open-ils.reporter-store" oils_obj:fieldmapper="reporter::super_simple_record" oils_persist:tablename="reporter.super_simple_record" reporter:label="Simple Record Extracts">
                <fields oils_persist:primary="id">
index 0e5fc98..dc3da26 100644 (file)
@@ -58,8 +58,7 @@ my $_output_handler_dispatch = {
         "prio" => 0,
         "code" => sub {
             $_[0]->content_type("text/html; charset=utf-8");
-            print html_ish_output( @_, 'FlatFielder2HTML.xsl' );
-            return Apache2::Const::OK;
+            return html_ish_output( @_, 'FlatFielder2HTML.xsl' );
         }
     },
     "application/xml" => {
@@ -115,14 +114,25 @@ sub data_to_xml {
     $fs->setAttribute("FS_key", $args->{key}) if $args->{key};
     $dom->setDocumentElement($fs);
 
+    my @columns;
+    my %column_labels;
+    if (@{$args->{columns}}) {
+        @columns = @{$args->{columns}};
+        if (@{$args->{labels}}) {
+            my @labels = @{$args->{labels}};
+            $column_labels{$columns[$_]} = $labels[$_] for (0..$#labels);
+        }
+    }
+
     my $rownum = 1;
     for my $i (@{$$args{data}}) {
         my $item = $dom->createElement("row");
         $item->setAttribute('ordinal', $rownum);
         $rownum++;
-        for my $k (keys %$i) {
+        @columns = keys %$i unless @columns;
+        for my $k (@columns) {
             my $val = $dom->createElement('column');
-            $val->setAttribute('name', $k);
+            $val->setAttribute('name', $column_labels{$k} || $k);
             $val->appendText($i->{$k});
             $item->addChild($val);
         }
@@ -214,6 +224,8 @@ sub handler {
     $args{key} = $cgi->param('key');
     $args{id_field} = $cgi->param('identifier');
     $args{label_field} = $cgi->param('label');
+    $args{columns} = [ $cgi->param('columns') ];
+    $args{labels} = [ $cgi->param('labels') ];
 
     my $fielder = OpenSRF::AppSession->create('open-ils.fielder');
     if ($args{map}) {
diff --git a/Open-ILS/src/templates/circ/hold_pull_list.tt2 b/Open-ILS/src/templates/circ/hold_pull_list.tt2
new file mode 100644 (file)
index 0000000..69a9128
--- /dev/null
@@ -0,0 +1,91 @@
+[% WRAPPER base.tt2 %]
+[% ctx.page_title = 'Hold Pull List' %]
+<script type="text/javascript">
+    dojo.require("dijit.form.Button");
+    dojo.require("openils.widget.OrgUnitFilteringSelect");
+    dojo.require("openils.widget.FlattenerGrid");
+
+    var map_extras = {
+        "copy_circ_lib": {
+            "path": "current_copy.circ_lib",
+            "filter": true
+        },
+        "call_number_sort_key": {
+            "path": "current_copy.call_number.label_sortkey",
+            "filter" :true
+        }
+    };
+
+    function set_grid_query_from_org_selector() {
+        grid.query = {
+            "copy_circ_lib": org_selector.attr("value")
+        };
+        grid.refresh();
+    }
+
+    function prepare_org_selector(perm) {
+        new openils.User().buildPermOrgSelector(
+            perm, org_selector, null,
+            function() {
+                dojo.connect(
+                    org_selector, "onChange", set_grid_query_from_org_selector
+                );
+                set_grid_query_from_org_selector();
+            }
+        );
+    }
+
+    openils.Util.addOnLoad(
+        function() {
+            prepare_org_selector("VIEW_HOLD");
+        }
+    );
+
+</script>
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <div dojoType="dijit.layout.ContentPane"
+         layoutAlign="top" class="oils-header-panel">
+        <div>Hold Pull List</div>
+        <div>
+            <button dojoType="dijit.form.Button"
+                onClick="grid.print();">Print Pull List</button>
+        </div>
+    </div>
+    <div class="oils-acq-basic-roomy">
+        <label for="org_selector">Show the pull list for:</label>
+        <select
+            id="org_selector" jsId="org_selector"
+            dojoType="openils.widget.OrgUnitFilteringSelect"
+            searchAttr="name" labelAttr="name">
+        </select>
+    </div>
+    <table
+        jsid="grid"
+        dojoType="openils.widget.FlattenerGrid"
+        columnPersistKey='"circ.hold_pull_list"'
+        autoHeight="10"
+        editOnEnter="false"
+        hideSelector="true"
+        editStyle="pane"
+        showLoadFilter="true"
+        fmClass="'ahopl'"
+        defaultSort="['copy_location_sort_order','call_number_sort_key']"
+        mapExtras="map_extras"
+        sortFieldReMap="{call_number_label: 'call_number_sort_key'}"
+        fetchLock="true"
+        query="{}">
+        <thead>
+            <tr>
+                <th field="barcode" fpath="current_copy.barcode"></th>
+                <th field="title" fpath="current_copy.call_number.record.simple_record.title">Title</th>
+                <th field="author" fpath="current_copy.call_number.record.simple_record.author">Author</th>
+                <th field="call_number_label" fpath="call_number_label"></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>
+</div>
+[% END %]
index e5cbdd7..700d3f2 100644 (file)
@@ -29,6 +29,7 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
         "offset": 0,
         "baseSort": null,
         "defaultSort": null,
+        "sortFieldReMap": null,
 
         "constructor": function(/* object */ args) {
             dojo.mixin(this, args);
@@ -51,7 +52,33 @@ if (!dojo._hasResource["openils.FlattenerStore"]) {
             );
         },
 
-        "_prepare_flattener_params": function(req) {
+        "_remap_sort": function(prepared_sort) {
+            if (this.sortFieldReMap) {
+                return prepared_sort.map(
+                    dojo.hitch(
+                        this, function(exp) {
+                            if (typeof exp == "object") {
+                                var key;
+                                for (key in exp)
+                                    break;
+                                var newkey = (key in this.sortFieldReMap) ?
+                                    this.sortFieldReMap[key] : key;
+                                var o = {};
+                                o[newkey] = exp[key];
+                                return o;
+                            } else {
+                                return (exp in this.sortFieldReMap) ?
+                                    this.sortFieldReMap[exp] : exp;
+                            }
+                        }
+                    )
+                );
+            } else {
+                return prepared_sort;
+            }
+        },
+
+        "_build_flattener_params": function(req) {
             var params = {
                 "hint": this.fmClass,
                 "ses": openils.User.authtoken
@@ -65,31 +92,38 @@ 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._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;
+                }
+
+                if (req.queryOptions.columns)
+                    params.columns = req.queryOptions.columns;
+                if (req.queryOptions.labels)
+                    params.labels = req.queryOptions.labels;
+
+                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);
             }
 
-            for (var key in params)
-                console.debug("flattener param " + key + " -> " + params[key]);
+//            for (var key in params)
+//                console.debug("flattener param " + key + " -> " + params[key]);
 
             return params;
         },
@@ -114,6 +148,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(
@@ -223,35 +345,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) {
@@ -296,41 +401,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;
         },
@@ -368,7 +453,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) {
@@ -404,17 +497,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 a18cd4a..0932970 100644 (file)
@@ -17,6 +17,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
             "columnReordering": true,
             "columnPersistKey": null,
             "showLoadFilter": false,    /* use FlattenerFilterDialog */
+            "fetchLock": false,
 
             /* These potential constructor arguments maybe useful to
              * FlattenerGrid in their own right, and are passed to
@@ -24,6 +25,7 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
             "fmClass": null,
             "fmIdentifier": null,
             "mapExtras": null,
+            "sortFieldReMap": null,
             "defaultSort": null,  /* whatever any part of the UI says will
                                      /replace/ this */
             "baseSort": null,     /* will contains what the columnpicker
@@ -158,6 +160,13 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                         var field_def =
                             fieldmapper.IDL.fmclasses[hint].field_map[field];
 
+                        if (!field_def) {
+                            throw new Error(
+                                "Lost our way in IDL at hint " + hint +
+                                ", field " + field
+                            );
+                        }
+
                         if (field_def["class"] && path.length) {
                             last_field = field;
                             last_hint = hint;
@@ -237,6 +246,22 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                 );
             },
 
+            "_columnOrderingAndLabels": function() {
+                var labels = [];
+                var columns = [];
+
+                this.views.views[0].structure.cells[0].forEach(
+                    function(c) {
+                        if (!c.field.match(/^\+/)) {
+                            labels.push(c.name);
+                            columns.push(c.field);
+                        }
+                    }
+                );
+
+                return {"labels": labels, "columns": columns};
+            },
+
             "constructor": function(args) {
                 dojo.mixin(this, args);
 
@@ -287,17 +312,22 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
 
             "_finishStartup": function(sortFields) {
 
-                this.setStore(
+                this._setStore( /* Seriously, let's leave this as _setStore. */
                     new openils.FlattenerStore({
                         "fmClass": this.fmClass,
                         "fmIdentifier": this.fmIdentifier,
                         "mapClause": (this.mapClause ||
                             this._cleanMapForStore(this._generateMap())),
                         "baseSort": this.baseSort,
-                        "defaultSort": this._mapCPSortFields(sortFields)
+                        "defaultSort": this._mapCPSortFields(sortFields),
+                        "sortFieldReMap": this.sortFieldReMap
+
                     }), this.query
                 );
 
+                if (!this.fetchLock)
+                    this._refresh(true);
+
                 // pick up any column label changes
                 this.columnPicker.reloadStructure();
 
@@ -352,6 +382,18 @@ if (!dojo._hasResource["openils.widget.FlattenerGrid"]) {
                 }
             },
 
+            "refresh": function() {
+                this.fetchLock = false;
+                this._refresh(/* isRender */ true);
+            },
+
+            "_fetch": function() {
+                if (this.fetchLock)
+                    return;
+                else
+                    return this.inherited(arguments);
+            },
+
             /* ******** below are methods mostly copied but
              * slightly changed from AutoGrid ******** */
 
@@ -662,6 +704,26 @@ 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 coal = this._columnOrderingAndLabels();
+                var req = {
+                    "query": this.query,
+                    "queryOptions": {
+                        "all": true,
+                        "columns": coal.columns,
+                        "labels": coal.labels
+                    },
+                    "onComplete": function(text) {
+                        openils.Util.printHtmlString(text);
+                    }
+                };
+
+                this.store.fetchToPrint(req);
             }
         }
     );
index e9385dd..40157fe 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>