[open-ils-commits] r15448 - in trunk/Open-ILS/web: css/skin/default js/dojo/openils/acq/nls js/ui/default/acq/common js/ui/default/acq/po templates/default/acq/po (senator)

svn at svn.open-ils.org svn at svn.open-ils.org
Thu Feb 4 12:21:40 EST 2010


Author: senator
Date: 2010-02-04 12:21:37 -0500 (Thu, 04 Feb 2010)
New Revision: 15448

Modified:
   trunk/Open-ILS/web/css/skin/default/acq.css
   trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
   trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js
   trunk/Open-ILS/web/js/ui/default/acq/po/search.js
   trunk/Open-ILS/web/templates/default/acq/po/search.tt2
Log:
Add an "virtual PO" view of PO search results in the Acquisitions module.

This extends the li_table primitive and uses it to display lineitems belonging
to many POs as if they were part of one large PO.



Modified: trunk/Open-ILS/web/css/skin/default/acq.css
===================================================================
--- trunk/Open-ILS/web/css/skin/default/acq.css	2010-02-04 14:23:54 UTC (rev 15447)
+++ trunk/Open-ILS/web/css/skin/default/acq.css	2010-02-04 17:21:37 UTC (rev 15448)
@@ -90,13 +90,32 @@
 .oils-acq-lineitem-attr-name {}
 .oils-acq-lineitem-attr-value {}
 #oils-acq-lineitem-marc-block { margin-top: 10px; padding: 6px; }
+.oils-acq-po-heading-po { font-weight: bold; }
 
+#oils-acq-metapo-summary {
+    border: 1px inset #ccc;
+    width: 400px;
+    margin-bottom: 8px;
+    margin-left: 8px;
+}
+#oils-acq-metapo-summary th, #oils-acq-metapo-summary td { padding: 3px; }
+#oils-acq-metapo-summary tbody th { font-weight: bold; }
+#oils-acq-metapo-summary thead th {
+    font-size: 110%;
+    font-weight: bold;
+    text-align: center;
+}
+#oils-acq-metapo-summary td { text-align: right; }
 
 #acq-lit-table {width:100%}
 #acq-lit-table th {padding:5px; font-weight: bold; text-align:left;}
 #acq-lit-table td {padding:2px;}
 .acq-lit-row { border-bottom: 1px solid #AAA; }
 .acq-lit-alt-row td { padding-left:30px; }
+.acq-lit-po-heading td { background-color: #ccc; border: 1px solid #000; }
+.acq-lit-po-heading td span { padding-left: 6px; padding-right: 6px; }
+.acq-lit-po-heading td span span { padding: 0; }
+.acq-lit-po-heading td span a[attr="name"] { font-weight: bold; }
 #acq-lit-info-tbody td {padding:5px;}
 #acq-lit-li-details-table {margin-top:20px;}
 #acq-lit-li-details-table td {padding:0px 3px 1px 3px;}
@@ -111,4 +130,3 @@
 .acq-lit-table-spacer { height:20px; }
 .acq-lit-row td[name="selector"] { width:1.5em; font-weight:bold; color:blue; font-size:110%;}
 #acq-lit-notes-tbody li { margin-bottom:10px; border:1px solid #aaa; -moz-border-radius: 5px 5px 5px 5px; }
-

Modified: trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
===================================================================
--- trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js	2010-02-04 14:23:54 UTC (rev 15447)
+++ trunk/Open-ILS/web/js/dojo/openils/acq/nls/acq.js	2010-02-04 17:21:37 UTC (rev 15448)
@@ -2,5 +2,7 @@
     'CREATE_PO_ASSETS_CONFIRM' : "This will create bibliographic, call number, and copy records for this purchase order in the ILS.\n\nContinue?",
     'ROLLBACK_PO_RECEIVE_CONFIRM' : "This will rollback receipt of all copies for this purchase order.\n\nContinue?",
     'XUL_RECORD_DETAIL_PAGE' : 'Record Details',
-    'DELETE_LI_COPIES_CONFIRM' : 'This will delete the last ${0} copies in the table.  Proceed?'
+    'DELETE_LI_COPIES_CONFIRM' : 'This will delete the last ${0} copies in the table.  Proceed?',
+    'NO_PO_RESULTS': "No results",
+    'PO_HEADING_ERROR' : "Unexpected problem building virtual combined PO"
 }

Modified: trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js
===================================================================
--- trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js	2010-02-04 14:23:54 UTC (rev 15447)
+++ trunk/Open-ILS/web/js/ui/default/acq/common/li_table.js	2010-02-04 17:21:37 UTC (rev 15448)
@@ -169,7 +169,7 @@
      * Inserts a single lineitem into the growing table of lineitems
      * @param {Object} li The lineitem object to insert
      */
-    this.addLineitem = function(li) {
+    this.addLineitem = function(li, skip_final_placement) {
         this.liCache[li.id()] = li;
 
         // sort the lineitem notes on edit_time
@@ -200,8 +200,13 @@
                 this.poCache[li.purchase_order()] ||
                 fieldmapper.standardRequest(
                     ['open-ils.acq', 'open-ils.acq.purchase_order.retrieve'],
-                    {params: [this.authtoken, li.purchase_order()]});
-            if(po) {
+                    {params: [
+                        this.authtoken, li.purchase_order(), {
+                            "flesh_price_summary": true,
+                            "flesh_lineitem_count": true
+                        }
+                    ]});
+            if(po && !this.isMeta) {
                 openils.Util.show(nodeByName('po', row), 'inline');
                 var link = nodeByName('po_link', row);
                 link.setAttribute('href', oilsBasePath + '/acq/po/view/' + li.purchase_order());
@@ -210,7 +215,7 @@
         }
 
         // show which picklist this lineitem is a member of
-        if(li.picklist() && this.isPO) {
+        if(li.picklist() && (this.isPO || this.isMeta)) {
             var pl = 
                 this.plCache[li.picklist()] = 
                 this.plCache[li.picklist()] || 
@@ -226,7 +231,11 @@
         }
 
         var countNode = nodeByName('count', row);
-        countNode.innerHTML = li.item_count() || 0;
+        var count = li.item_count() || 0;
+        if (typeof(this._copy_count_cb) == "function") {
+            this._copy_count_cb(li.id(), count);
+        }
+        countNode.innerHTML = count;
         countNode.id = 'acq-lit-copy-count-label-' + li.id();
 
         // lineitem state
@@ -260,8 +269,12 @@
             }
         }
 
-        self.tbody.appendChild(row);
-        self.selectors.push(dojo.query('[name=selectbox]', row)[0]);
+        if (!skip_final_placement) {
+            self.tbody.appendChild(row);
+            self.selectors.push(dojo.query('[name=selectbox]', row)[0]);
+        } else {
+            return row;
+        }
     };
 
     /**
@@ -851,6 +864,10 @@
             }
         }
 
+        if (typeof(this._copy_count_cb) == "function") {
+            this._copy_count_cb(liId, total);
+        }
+
         dojo.byId('acq-lit-copy-count-label-' + liId).innerHTML = total;
 
         if(copies.length == 0)

Modified: trunk/Open-ILS/web/js/ui/default/acq/po/search.js
===================================================================
--- trunk/Open-ILS/web/js/ui/default/acq/po/search.js	2010-02-04 14:23:54 UTC (rev 15447)
+++ trunk/Open-ILS/web/js/ui/default/acq/po/search.js	2010-02-04 17:21:37 UTC (rev 15448)
@@ -1,5 +1,6 @@
 dojo.require('dijit.form.Form');
 dojo.require('dijit.form.Button');
+dojo.require('dijit.form.CheckBox');
 dojo.require('dijit.form.FilteringSelect');
 dojo.require('dijit.form.NumberTextBox');
 dojo.require('dojo.data.ItemFileWriteStore');
@@ -11,6 +12,9 @@
 dojo.require('openils.widget.AutoFieldWidget');
 dojo.require('openils.PermaCrud');
 
+var metaPO;
+var _last_fields;
+var general_po_search_opts = {"order_by": {"acqpo": "edit_time DESC"}};
 
 function getPOOwner(rowIndex, item) {
     if(!item) return '';
@@ -19,7 +23,15 @@
 }
 
 function doSearch(fields) {
-    
+    _last_fields = dojo.clone(fields); /* Save for re-use */
+    var metapo_view = false;
+
+    /* Remove the metapo_view field from 'fields'... we'll use it later */
+    if (fields.metapo_view && fields.metapo_view[0]) {
+        metapo_view = true;
+        delete fields.metapo_view;
+    }
+
     if(isNaN(fields.id)) {
         delete fields.id;
         for(var k in fields) {
@@ -36,8 +48,15 @@
     for(var k in fields) some = true;
     if(!some) fields.id = {'!=' : null};
 
-    poGrid.resetStore();
-    poGrid.loadAll({order_by:{acqpo : 'edit_time DESC'}}, fields);
+    if (metapo_view) {
+        openils.Util.hide("holds_po_grid");
+        loadMetaPO(fields);
+    } else {
+        if (metaPO) metaPO.myHide();
+        openils.Util.show("holds_po_grid");
+        poGrid.resetStore();
+        poGrid.loadAll(general_po_search_opts, fields);
+    }
 }
 
 function loadForm() {
@@ -61,4 +80,256 @@
     doSearch({ordering_agency : openils.User.user.ws_ou()});
 }
 
+function loadMetaPO(fields) {
+    var pcrud = new openils.PermaCrud();
+    var po_list = pcrud.search("acqpo", fields, general_po_search_opts);
+    if (!po_list || !po_list.length) {
+        alert(localeStrings.NO_PO_RESULTS);
+    } else {
+        if (!metaPO) {
+            metaPO = new AcqLiTable();
+
+            /* We need to know the width (in cells) of the template row for
+             * the LI table, and we don't want to hardcode it here. */
+            metaPO.n_cells = dojo.query("> td", metaPO.rowTemplate).length;
+
+            metaPO._copy_count_cb = function(liId, count) {
+                var poId = this.liCache[liId].purchase_order();
+
+                if (this.copy_counts[poId] == undefined)
+                    this.copy_counts[poId] = {};
+                this.copy_counts[poId][liId] = count;
+
+                this.renderCopyCounts(poId);
+                this.renderSummary("copies");
+            };
+            metaPO.myHide = function() {
+                this.hide();
+                openils.Util.hide("oils-acq-holds-metapo-summary");
+            };
+            metaPO.renderSummary = function(part) {
+                var self = this;
+                /* The idea here will be that if "part" is defined, we'll
+                 * just update that part of the metaPO summary, otherwise,
+                 * the whole thing. */
+                if (part != undefined) {
+                    var target = dojo.byId("oils-acq-metapo-summary-" + part);
+                    switch (part) {
+                        case "copies":
+                            target.innerHTML = self.copiesTotal();
+                            break;
+                        case "po":
+                            target.innerHTML = self.working_po_list.length;
+                            break;
+                        default:
+                            /* assume a field on the acqpo's themselves */
+                            target.innerHTML = self.anyFieldTotal(part);
+                            break;
+                    }
+                } else {
+                    openils.Util.show("oils-acq-holds-metapo-summary");
+                    self.totalable_fields.forEach(
+                        function(f) { self.renderSummary(f); }
+                    );
+                }
+            };
+            metaPO.anyFieldTotal = function(field) {
+                var self = this;
+                return self.working_po_list.reduce(
+                    /* working_po_list contains unfleshed, acqpo's, so we must
+                     * find the same PO in the poCache */
+                    function(p, c) {
+                        c = self.poCache[c.id()][field]();
+                        return p + Number(c);
+                    }, 0
+                );
+            };
+            metaPO.renderCopyCounts = function(poId) {
+                try {
+                    dojo.query("td#oils-acq-po-heading-" + poId +
+                        ' span span[attr="copies"]')[0].innerHTML =
+                            this.copiesByPOId(poId);
+                } catch (E) {
+                    ;
+                }
+            };
+            metaPO.sectionHeadingById = function(id) {
+                var headings = dojo.query("#po-heading-" + id, this.tbody);
+                if (headings.length != 1) {
+                    alert(localeStrings.PO_HEADING_ERROR);
+                    return undefined;
+                } else {
+                    return headings[0];
+                }
+            };
+            metaPO.sectionHeadingByPOId = function(poId) {
+                return this.sectionHeadingById(this.sections_by_poid[poId]);
+            };
+            metaPO.addSection = function(po) {
+                var s = this.sections_by_poid[po.id()] = this.sections++;
+
+                this.tbody.appendChild(
+                    dojo.create("tr", {
+                        "class": "acq-lit-po-heading", "id": "po-heading-" + s
+                    })
+                );
+
+                return s;
+            };
+            metaPO.addLineitemToSection = function(li, section) {
+                dojo.place(
+                    this.addLineitem(li, true /* skip_final_placement */),
+                    this.sectionHeadingById(section),
+                    "after"
+                );
+            };
+            metaPO.generateActivator = function(id) {
+                return function() {
+                    progressDialog.show(true);
+                    try {
+                        fieldmapper.standardRequest(
+                            ["open-ils.acq",
+                                "open-ils.acq.purchase_order.activate"], {
+                                "async": true,
+                                "params": [openils.User.authtoken, id],
+                                "oncomplete": function() {
+                                    progressDialog.hide();
+                                    doSearch(_last_fields);
+                                }
+                            }
+                        );
+                    } catch (E) {
+                        progressDialog.hide();
+                        alert(E); /* XXX */
+                    }
+                };
+            };
+            metaPO.renderHeading = function(poId) {
+                var self = this;
+                var td = dojo.create("td", {"colspan": self.n_cells});
+                td.id = "oils-acq-po-heading-" + poId;
+
+                /* Build our HTML structure from the template... */
+                dojo.query("> span", "oils-acq-po-heading-template").forEach(
+                    function(s) { td.appendChild(s.cloneNode(true)); }
+                );
+
+                /* Some fields straight from the PO object... */
+                self.po_fields_for_display.forEach(
+                    function(f) {
+                        dojo.query('[attr="' + f + '"]', td)[0].innerHTML =
+                            self.poCache[poId][f]();
+                    }
+                );
+
+                /* The name field needs special treatment: it's a link */
+                dojo.attr(
+                    dojo.query('a[attr="name"]', td)[0],
+                    "href",
+                    oilsBasePath + '/acq/po/view/' + poId
+                );
+
+                /* Show an "activate" link, or not, based on "state"... */
+                var a = dojo.query('a[attr="activator"]', td)[0];
+                if (self.poCache[poId].state() == "pending") {
+                    a.onclick = self.generateActivator(poId);
+                    openils.Util.show(a, "inline");
+                } else {
+                    openils.Util.hide(a);
+                }
+
+                /* Put the new heading cell in place... */
+                dojo.place(td, self.sectionHeadingByPOId(poId), "only");
+
+                /* And finally, render copy info (must happen _after_ heading
+                 * is attached to the DOM tree */
+                this.renderCopyCounts(poId);
+            };
+            metaPO.copiesByPOId = function(poId) {
+                if (!this.copy_counts[poId]) return undefined;
+                var total = 0;
+                for (var liId in this.copy_counts[poId]) {
+                    total += this.copy_counts[poId][liId];
+                }
+                return total;
+            };
+            metaPO.copiesTotal = function() {
+                var total = 0;
+                for (var poId in this.copy_counts)
+                    total += this.copiesByPOId(poId);
+                return total;
+            };
+            metaPO.myReset = function() {
+                this.isMeta = true;
+                this.sections = 0;
+                this.sections_by_poid = {};
+                this.copy_counts = {};
+                this.po_fields_for_display = [
+                    "name", "lineitem_count", "amount_encumbered",
+                    "amount_spent", "state"
+                ];
+                this.totalable_fields = [
+                    "po", "lineitem_count", "copies",
+                    "amount_encumbered", "amount_spent"
+                ];
+                openils.Util.hide("oils-acq-holds-metapo-summary");
+            };
+            metaPO.populate = function(list) {
+                var self = this;
+                var done = 0;
+
+                self.working_po_list = [];
+
+                progressDialog.show(true);
+                list.forEach(function(po) {
+                    var sec = self.addSection(po);
+                    fieldmapper.standardRequest(
+                        ["open-ils.acq", "open-ils.acq.lineitem.search"], {
+                            "async": true,
+                            "params": [
+                                openils.User.authtoken,
+                                {"purchase_order": po.id()},
+                                {"flesh_attrs": true, "flesh_notes": true}
+                            ],
+                            "onresponse": function(r) {
+                                var li = openils.Util.readResponse(r);
+                                if (li) /* sometimes empty string: disregard */
+                                    self.addLineitemToSection(li, sec);
+                            },
+                            "oncomplete": function(r) {
+                                self.working_po_list.push(po);
+                                self.renderHeading(po.id());
+                                self.renderSummary();
+                                /* This mechanism avoids calling .show() too
+                                 * often or before results are ready, and
+                                 * thus smooths out DOM rendering glitches. */
+                                if (++done >= list.length) {
+                                    done = -1;
+                                    self.show("list");
+                                    progressDialog.hide();
+                                }
+                            }
+                        }
+                    );
+                });
+                /* This mechanism sees to it that we call .show() at least once
+                 * even if the search result population seems to be timing
+                 * out or failing. */
+                setTimeout(
+                    function() {
+                        if (done != -1) {
+                            self.show("list");
+                            progressDialog.hide();
+                        }
+                    }, 10000    /* 10 seconds: make this configurable? */
+                );
+            };
+        }
+
+        metaPO.reset();
+        metaPO.myReset();
+        metaPO.populate(po_list);
+    }
+}
+
 openils.Util.addOnLoad(loadForm);

Modified: trunk/Open-ILS/web/templates/default/acq/po/search.tt2
===================================================================
--- trunk/Open-ILS/web/templates/default/acq/po/search.tt2	2010-02-04 14:23:54 UTC (rev 15447)
+++ trunk/Open-ILS/web/templates/default/acq/po/search.tt2	2010-02-04 17:21:37 UTC (rev 15448)
@@ -4,6 +4,15 @@
     <div id='oils-acq-list-header-label'>PO Search</div>
 </div>
 
+<div id="oils-acq-po-heading-template" class="hidden">
+    <span>Purchase Order: <a attr="name"></a></span>
+    <span>Total Lineitems: <span attr="lineitem_count"></span></span>
+    <span>Total Encumbered: $<span attr="amount_encumbered"></span></span>
+    <span>Total Spent: $<span attr="amount_spent"></span></span>
+    <span>Total Copies: <span attr="copies"></span></span>
+    <span>Status: <span attr="state"></span></span>
+    <span><a class="hidden" attr="activator" href="javascript:void(0);">Activate Order</a></span>
+</div>
 <!-- load the page-specific JS -->
 <script src='[% ctx.media_prefix %]/js/ui/default/acq/po/search.js'> </script>
 
@@ -36,8 +45,12 @@
                     identifier:"value",
                     label: "name",
                     items: [
+                        /* FIXME This is probably not the correct final list of 
+                        possible states */
                         {name:"New", value:'new'},
-                        {name:"In Process", value:'in-process'}
+                        {name:"In Process", value:'in-process'},
+                        {name:"Pending", value:'pending'},
+                        {name:"On order", value:'on-order'}
                     ]
                 }
             });
@@ -55,9 +68,14 @@
 
         <span dojoType='dijit.form.Button' type='submit'>Search</span>
     </div>
+    <div class="oils-acq-basic-form-div">
+        <input dojoType="dijit.form.CheckBox" value="1" name="metapo_view"
+            id="metapo_view" type="checkbox" />
+        <label for="metapo_view">Show results as a virtual combined PO</label>
+    </div>
 </form>
 <br/>
-<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client" id="holds_po_grid">
     <table 
             id="po-grid" 
             autoHeight='true'
@@ -80,7 +98,38 @@
             </tr> 
         </thead> 
     </table>     
-    <div comment='dojo-needs-me'/>
+    <div comment='dojo-needs-me'></div>
 </div>
+<div id="oils-acq-holds-metapo-summary" class="hidden">
+    <table id="oils-acq-metapo-summary">
+        <thead>
+            <tr>
+                <th colspan="2">Results Summary</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr>
+                <th>Total Purchase Orders:</th>
+                <td id="oils-acq-metapo-summary-po"></td>
+            </tr>
+            <tr>
+                <th>Total Lineitems:</th>
+                <td id="oils-acq-metapo-summary-lineitem_count"></td>
+            </tr>
+            <tr>
+                <th>Total Copies:</th>
+                <td id="oils-acq-metapo-summary-copies"></td>
+            </tr>
+            <tr>
+                <th>Total Encumbered:</th>
+                <td>$<span id="oils-acq-metapo-summary-amount_encumbered"></span></td>
+            </tr>
+            <tr>
+                <th>Total Spent:</th>
+                <td>$<span id="oils-acq-metapo-summary-amount_spent"></span></td>
+            </tr>
+        </tbody>
+    </table>
+</div>
+[% INCLUDE 'default/acq/common/li_table.tt2' %]
 [% END %]
-



More information about the open-ils-commits mailing list