Serials: add ability to print routing lists from the batch receive interface
authorsenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Tue, 23 Nov 2010 21:40:36 +0000 (21:40 +0000)
committersenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Tue, 23 Nov 2010 21:40:36 +0000 (21:40 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@18840 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/OpenILS/Application/Serial.pm
Open-ILS/web/js/dojo/openils/XUL.js
Open-ILS/web/js/ui/default/serial/print_routing_list_users.js [new file with mode: 0644]
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/web/templates/default/serial/print_routing_list_users.tt2 [new file with mode: 0644]
Open-ILS/xul/staff_client/server/locale/en-US/serial.properties
Open-ILS/xul/staff_client/server/serial/batch_receive.js
Open-ILS/xul/staff_client/server/serial/batch_receive_overlay.xul
Open-ILS/xul/staff_client/server/skin/serial.css

index 444429f..1393a2a 100644 (file)
@@ -2806,7 +2806,7 @@ __PACKAGE__->register_method(
             "(with card and home_ou) for a given stream ID, sorted by pos",
         "params" => [
             {"desc" => "Authtoken", "type" => "string"},
-            {"desc" => "Stream ID", "type" => "number"},
+            {"desc" => "Stream ID (int or array of ints)", "type" => "mixed"},
         ],
         "return" => {
             "desc" => "Stream of routing list users", "type" => "object",
@@ -2818,8 +2818,6 @@ __PACKAGE__->register_method(
 sub get_routing_list_users {
     my ($self, $client, $auth, $stream_id) = @_;
 
-    return undef unless $stream_id = int $stream_id; # sic, assignment
-
     my $e = new_editor("authtoken" => $auth);
     return $e->die_event unless $e->checkauth;
 
@@ -2846,9 +2844,11 @@ sub get_routing_list_users {
 
     $e->disconnect;
 
-    # Now we can strip the stream/distribution info (only used for perm
-    # checking) and send back the srlu's to the caller.
-    $client->respond($_) for map { $_->stream($_->stream->id); $_ } @$users;
+    my @users = map { $_->stream($_->stream->id); $_ } @$users;
+    @users = sort { $a->stream cmp $b->stream } @users if
+        ref $stream_id eq "ARRAY";
+
+    $client->respond($_) for @users;
 
     undef;
 }
index ec1e765..8e367da 100644 (file)
@@ -36,7 +36,9 @@ if(!dojo._hasResource["openils.XUL"]) {
         xulG.new_tab(path, tabInfo, options);
     }
 
-    openils.XUL.newTabEasy = function(url, tab_name, extra_content_params) {
+    openils.XUL.newTabEasy = function(
+        url, tab_name, extra_content_params, wrap_in_browser
+    ) {
         var content_params = {
             "session": openils.User.authtoken,
             "authtime": openils.User.authtime
@@ -52,9 +54,20 @@ if(!dojo._hasResource["openils.XUL"]) {
         if (extra_content_params)
             dojo.mixin(content_params, extra_content_params);
 
-        xulG.new_tab(
-            xulG.url_prefix(url), {"tab_name": tab_name}, content_params
-        );
+        var loc = xulG.url_prefix(url);
+
+        if (wrap_in_browser) {
+            loc = urls.XUL_BROWSER + "?url=" + window.escape(loc);
+            content_params = dojo.mixin(
+                {
+                    "no_xulG": false, "show_print_button": true,
+                    "show_nav_buttons": true,
+                    "passthru_content_params": extra_content_params
+                }, content_params
+            );
+        }
+
+        xulG.new_tab(loc, {"tab_name": tab_name}, content_params);
     };
 
     /**
diff --git a/Open-ILS/web/js/ui/default/serial/print_routing_list_users.js b/Open-ILS/web/js/ui/default/serial/print_routing_list_users.js
new file mode 100644 (file)
index 0000000..014f645
--- /dev/null
@@ -0,0 +1,147 @@
+dojo.require("dojo.string");
+
+var list_renderer;
+
+function n(name, ctx) { return dojo.query("[name='" + name + "']", ctx)[0]; }
+
+function ListRenderer() {
+    var self = this;
+
+    this.render = function() {
+        for (var i = 0; i < this.streams.length; i++) {
+            var stream = this.streams[i];
+            var list = dojo.clone(this.list_template);
+            n("title", list).innerHTML = this.mvr.title();
+            n("issuance_label", list).innerHTML = this.issuance.label();
+            n("distribution_holding_lib", list).innerHTML =
+                stream.distribution().holding_lib().shortname();
+            n("distribution_label", list).innerHTML =
+                stream.distribution().label();
+            if (stream.routing_label()) {
+                n("stream_routing_label", list).innerHTML =
+                    stream.routing_label();
+                openils.Util.show(
+                    n("stream_routing_label", list), "inline"
+                );
+            } else {
+                n("stream_id", list).innerHTML = stream.id();
+                openils.Util.show(n("stream_id_container", list), "inline");
+            }
+
+            this.render_users(stream, list);
+
+            if (i) {
+                dojo.create(
+                    "hr",
+                    {"style": "page-break-after: always"}, this.target, "last"
+                );
+            }
+
+            dojo.place(list, this.target, "last");
+        }
+
+        return this; /* for chaining */
+    };
+
+    this.render_users = function(stream, list) {
+        for (var i = 0; i < this.users_by_stream[stream.id()].length; i++) {
+            var user = this.users_by_stream[stream.id()][i];
+            var node = dojo.clone(this.user_template);
+
+            if (user.reader()) {
+                n("barcode", node).innerHTML = user.reader().card().barcode();
+                n("name", node).innerHTML = dojo.string.substitute(
+                    "${0}, ${1} ${2}", [
+                        user.reader().family_name(),
+                        user.reader().first_given_name(),
+                        user.reader().second_given_name()
+                    ].map(function(n) { return n || ""; })
+                );
+                n("ou", node).innerHTML = user.reader().home_ou().shortname();
+                openils.Util.show(n("reader_container", node), "inline");
+            } else if (user.department()) {
+                n("department", node).innerHTML = user.department();
+                openils.Util.show(n("department_container", node), "inline");
+            }
+
+            if (user.note()) {
+                n("note", node).innerHTML = user.note();
+                openils.Util.show(n("note_container", node), "inline");
+            }
+
+            dojo.place(node, n("users", list), "last");
+        }
+    };
+
+    this.print = function() {
+        this.print_target.print();
+    }
+
+    this._sort_users = function() {
+        this.users_by_stream = {};
+        this.users.forEach(
+            function(user) {
+                var key = user.stream();
+                if (!self.users_by_stream[key])
+                    self.users_by_stream[key] = [];
+                self.users_by_stream[key].push(user);
+            }
+        );
+    };
+
+    /* Unfortunately, when we print the main window with dijits
+     * wrapping everything, the page-break-* CSS properties don't work
+     * inside of there, so we need an iframe to print from.
+     */
+    this._prepare_iframe = function() {
+        var iframe = dojo.create(
+            "iframe", {
+                "src": "", "width": "100%", "height": "500", "frameborder": 0
+            }, "iframe_in_here", "only"
+        );
+
+        iframe.contentWindow.document.open();
+        iframe.contentWindow.document.write(
+            "<html><head><style type='text/css'>" +
+            ".item-title { font-size: 130%; font-weight: bold; }\n" +
+            ".item-issuance-label { font-size: 120%; }\n" +
+            ".item-dist-and-stream { font-size: 110%; }\n" +
+            ".hidden { display: none; }\n" +
+            "</style></head>\n<body></body></html>"
+        );
+        iframe.contentWindow.document.close();
+        this.target = iframe.contentWindow.document.body;
+        this.print_target = iframe.contentWindow;
+    };
+
+    this._init = function(data) {
+        this.user_template = dojo.byId("user_template");
+        this.user_template.removeAttribute("id");
+        this.user_template.parentNode.removeChild(this.user_template);
+
+        this.list_template = dojo.byId("list_template");
+        this.list_template.removeAttribute("id");
+        this.list_template.parentNode.removeChild(this.list_template);
+
+        dojo.mixin(this, data);
+
+        this._sort_users();
+        this._prepare_iframe();
+    }
+
+    this._init.apply(this, arguments);
+}
+
+openils.Util.addOnLoad(
+    function() {
+        if (!xulG) {
+            alert(
+                "This interface is not designed for use outside " +
+                "the staff client." /* XXX i18n */
+            );
+        } else {
+            list_renderer = new ListRenderer(xulG.routing_list_data);
+            list_renderer.render().print();
+        }
+    }
+);
index c0e49b5..28cc0b9 100644 (file)
 <!ENTITY staff.serial.batch_receive.note "Note">
 <!ENTITY staff.serial.batch_receive.location "Copy Location">
 <!ENTITY staff.serial.batch_receive.price "Price">
+<!ENTITY staff.serial.batch_receive.print_routing "Routing List">
+<!ENTITY staff.serial.batch_receive.print_routing.tooltip "Print this item's routing list upon receipt, if it has one.">
 <!ENTITY staff.serial.batch_receive.receive "Receive?">
 <!ENTITY staff.serial.batch_receive.auto_generate "Auto-generate?">
 <!ENTITY staff.serial.batch_receive.recieve_selected "Receive Selected Items">
diff --git a/Open-ILS/web/templates/default/serial/print_routing_list_users.tt2 b/Open-ILS/web/templates/default/serial/print_routing_list_users.tt2
new file mode 100644 (file)
index 0000000..c96da83
--- /dev/null
@@ -0,0 +1,38 @@
+[% WRAPPER default/base.tt2 %]
+[% ctx.page_title = "Serial Routing List" %]
+<div class="hidden">
+    <div id="list_template">
+        <div class="item-title" name="title"></div>
+        <div class="item-issuance-label" name="issuance_label"></div>
+        <div class="item-dist-and-stream">
+            (<span name="distribution_holding_lib"></span>)
+            <span name="distribution_label"></span> /
+            <span name="stream_routing_label"></span>
+            <em name="stream_id_container">
+                Unlabeled stream ID #<span name="stream_id"></span>
+            </em>
+        </div>
+        <ol name="users">
+            <li id="user_template">
+                <span class="hidden" name="reader_container">
+                    Reader: <span name="barcode"></span> /
+                    <span name="name"></span>
+                    (<span name="ou"></span>)
+                </span>
+                <span class="hidden" name="department_container">
+                    Department: <span name="department"></span>
+                </span>
+                <span class="hidden" name="note_container">
+                    <br />&nbsp; <em name="note"></em>
+                </span>
+            </li>
+        </ol>
+    </div>
+</div>
+<div style="padding: 1em 0;">
+    <button onclick="list_renderer.print()" accesskey="P"><u>P</u>rint</button>
+</div>
+<div id="iframe_in_here"></div>
+<script type="text/javascript"
+    src="[% ctx.media_prefix %]/js/ui/default/serial/print_routing_list_users.js"></script>
+[% END %]
index 9df7045..cda2389 100644 (file)
@@ -85,6 +85,7 @@ batch_receive.receive_time_note=Receive-time Note
 batch_receive.cn_for_lib=Do you want to use this call number at %1$s?\nIt doesn't exist there, and it will have to be created.
 batch_receive.missing_units=You have not provided barcodes and call numbers for all of the selected items.  Choose OK to receive those items anyway, or choose Cancel to supply the missing information.
 batch_receive.missing_cn=You cannot assign a barcode without selecting a call number. Please correct the non-conforming units.
+batch_receive.print_routing_list_users=Print Routing List
 pattern_wizard.enumeration.a=First level
 pattern_wizard.enumeration.b=Second level
 pattern_wizard.enumeration.c=Third level
index 8389166..7cd5137 100644 (file)
@@ -4,6 +4,7 @@ dojo.require("dojo.cookie");
 dojo.require("openils.Util");
 dojo.require("openils.User");
 dojo.require("openils.CGI");
+dojo.require("openils.XUL");
 dojo.require("openils.PermaCrud");
 
 var batch_receiver;
@@ -56,6 +57,7 @@ function BatchReceiver() {
         this._prepared_call_number_controls = {};
         this._location_by_lib = {};
         this._copy_template_cache = {};
+        this._wants_print_routing = {};
 
         /* empty the entry receiving table if we're starting over */
         if (this.item_cache) {
@@ -346,20 +348,37 @@ function BatchReceiver() {
         return menulist;
     };
 
+    this._build_print_routing_toggle = function(item) {
+        var start = true;
+        var checkbox = dojo.create(
+            "checkbox", {
+                "oncommand": function(ev) {
+                       self._print_routing(item.id(), ev.target.checked);
+                },
+                "checked": start.toString()
+            }
+        );
+        this._print_routing(item.id(), start);
+        return checkbox;
+    }
+
     this._build_receive_toggle = function(item) {
         return dojo.create(
             "checkbox", {
                 "oncommand": function(ev) {
                        self._disable_row(item.id(), !ev.target.checked);
                 },
-                "checked": "true"
+                "checked": "true",
+                "name": "receive_" + item.id()
             }
         );
     }
 
     this._disable_row = function(item_id, disabled) {
         var row = this.rows[item_id];
-        dojo.query("textbox,menulist", row).forEach(
+        dojo.query(
+            "textbox,menulist,checkbox:not([name^='receive_'])", row
+        ).forEach(
             function(element) { element.disabled = disabled; }
         );
         this._row_disabled(row, disabled);
@@ -368,7 +387,7 @@ function BatchReceiver() {
     this._row_disabled = function(row, disabled) {
         if (typeof(row) == "string") row = this.rows[row];
 
-        var checkbox = dojo.query("checkbox", row)[0];
+        var checkbox = dojo.query("checkbox", row)[1];
 
         if (typeof(disabled) != "undefined")
             checkbox.checked = !disabled;
@@ -376,6 +395,19 @@ function BatchReceiver() {
         return !checkbox.checked;
     };
 
+    this._row_print_routing_disabled = function(row, disabled) {
+        if (typeof(row) == "string") row = this.rows[row];
+
+        var checkbox = dojo.query("checkbox", row)[0];
+
+        if (typeof(disabled) != "undefined") {
+            checkbox.checked = !disabled;
+            checkbox.doCommand();
+        }
+
+        return !checkbox.checked;
+    };
+
     this._row_field_value = function(row, field, value) {
         if (typeof(row) == "string") row = this.rows[row];
 
@@ -395,6 +427,10 @@ function BatchReceiver() {
         }
     }
 
+    this._print_routing = function(id, value) {
+        this._wants_print_routing[id] = value;
+    };
+
        this._user_wants_autogen = function() {
         return dojo.byId("autogen_barcodes").checked;
     };
@@ -511,6 +547,32 @@ function BatchReceiver() {
         }
     };
 
+    this.print_routing_lists = function(streams) {
+        fieldmapper.standardRequest(
+            ["open-ils.serial",
+                "open-ils.serial.routing_list_users.fleshed_and_ordered.atomic"],{
+                "params": [
+                    this.authtoken, streams.map(function(o) { return o.id(); })
+                ],
+                "async": false,
+                "oncomplete": function(r) {
+                    if ((r = openils.Util.readResponse(r)) && r.length) {
+                        openils.XUL.newTabEasy(
+                            "/eg/serial/print_routing_list_users",
+                            S("print_routing_list_users"), {
+                                "show_print_button": false, /* we supply one */
+                                "routing_list_data": {
+                                    "streams": streams, "mvr": self.bibdata.mvr,
+                                    "issuance": self.issuance, "users": r
+                                }
+                            }, true /* wrap_in_browser */
+                        );
+                    }
+                }
+            }
+        );
+    };
+
     this.bib_lookup = function(bib_search_term, evt, is_actual_id, sub_id) {
         if (evt && evt.keyCode != 13) return;
 
@@ -791,6 +853,12 @@ function BatchReceiver() {
         }
     };
 
+    this.toggle_all_print_routing = function(checked) {
+        for (var id in this.rows) {
+            this._row_print_routing_disabled(id, !checked);
+        }
+    };
+
     this.build_batch_entry_row = function() {
         var row = dojo.byId("entry_batch_row");
 
@@ -820,6 +888,17 @@ function BatchReceiver() {
             this.batch_controls.price = dojo.create("textbox", {"size": 9})
         );
 
+        node_by_name("print_routing", row).appendChild(
+            dojo.create(
+                "checkbox", {
+                    "oncommand": function(ev) {
+                        self.toggle_all_print_routing(ev.target.checked);
+                    },
+                    "checked": "true"
+                }
+            )
+        );
+
         node_by_name("receive", row).appendChild(
             dojo.create(
                 "checkbox", {
@@ -896,6 +975,7 @@ function BatchReceiver() {
         n("circ_modifier").appendChild(this._build_circ_modifier_dropdown());
         n("call_number").appendChild(this._build_call_number_control(item));
         n("price").appendChild(dojo.create("textbox", {"size": 9}));
+        n("print_routing").appendChild(this._build_print_routing_toggle(item));
         n("receive").appendChild(this._build_receive_toggle(item));
 
         this.entry_tbody.appendChild(row);
@@ -966,8 +1046,17 @@ function BatchReceiver() {
                 "async": true,
                 "oncomplete": function(r) {
                     try {
-                        while (item_id = openils.Util.readResponse(r))
+                        var streams_for_printing = [];
+                        while (item_id = openils.Util.readResponse(r)) {
+                            if (self._wants_print_routing[item_id]) {
+                                streams_for_printing.push(
+                                    self.item_cache[item_id].stream()
+                                );
+                            }
                             self.finish_receipt(item_id);
+                        }
+                        if (streams_for_printing.length)
+                            self.print_routing_lists(streams_for_printing);
                     } catch (E) {
                         alert(E);
                     }
index adedc30..5130387 100644 (file)
                                 <h:th name="price">
                                     &staff.serial.batch_receive.price;
                                 </h:th>
+                                <h:th name="print_routing">
+                                    <description
+                                        id="print_routing_desc"
+                                        value="&staff.serial.batch_receive.print_routing;"
+                                        tooltiptext="&staff.serial.batch_receive.print_routing.tooltip;" />
+                                </h:th>
                                 <h:th>
                                     &staff.serial.batch_receive.receive;
                                 </h:th>
                                 <h:td name="note"></h:td>
                                 <h:td name="location" align="center"></h:td>
                                 <h:td name="price"></h:td>
+                                <h:td name="print_routing" align="center"></h:td>
                                 <h:td name="receive" align="center"></h:td>
                                 <h:td name="apply"></h:td>
                             </h:tr>
                             <h:tr>
-                                <h:td colspan="8">
+                                <h:td colspan="9">
                                     <h:hr size="4" />
                                 </h:td>
                             </h:tr>
                                 <h:td name="note"></h:td>
                                 <h:td name="location" align="center"></h:td>
                                 <h:td name="price"></h:td>
+                                <h:td name="print_routing" align="center"></h:td>
                                 <h:td name="receive" align="center"></h:td>
                             </h:tr>
                         </h:tbody>
index 1c6bfa6..2ebfa2b 100644 (file)
@@ -20,3 +20,4 @@ checkbox:focus:not([label]) .checkbox-label-box {
     border: none;
 }
 .padded_bottom { padding-bottom: 10px; }
+description#print_routing_desc { margin: 0; padding: 0 1em; }