[open-ils-commits] [GIT] Evergreen ILS branch master updated. f6ac572987d68c443e16998aeb633990c37e389b

Evergreen Git git at git.evergreen-ils.org
Wed Mar 13 15:54:21 EDT 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  f6ac572987d68c443e16998aeb633990c37e389b (commit)
       via  76d2e15b02e8c65659408e82c3ddfe7744ca24cc (commit)
      from  78b93b1e79d4139b2aefd52ab827dc37ed3133e8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit f6ac572987d68c443e16998aeb633990c37e389b
Author: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
Date:   Wed Mar 13 10:43:40 2013 -0400

    Acq: two tweaks to UI improvements in previous commit
    
    Suggested by Bill Erickson:
      * Prevent interface locking up with the Mark Received/Unreceived
        actions when nothing is selected
      * Make new lineitem batch receive/rollback API methods actually stream
    
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
index ca49a79..d8f7e8d 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
@@ -2158,6 +2158,7 @@ sub receive_lineitem_api {
 __PACKAGE__->register_method(
 	method => 'receive_lineitem_batch_api',
 	api_name	=> 'open-ils.acq.lineitem.receive.batch',
+    stream => 1,
 	signature => {
         desc => 'Mark lineitems as received',
         params => [
@@ -2344,6 +2345,7 @@ sub rollback_receive_lineitem_api {
 __PACKAGE__->register_method(
     method    => 'rollback_receive_lineitem_batch_api',
     api_name  => 'open-ils.acq.lineitem.receive.rollback.batch',
+    stream => 1,
     signature => {
         desc   => 'Mark a list of lineitems as Un-received',
         params => [
diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
index ad82f7b..1f15d16 100644
--- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
+++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
@@ -89,5 +89,6 @@
     "CREATE_PO_INVALID": "A purchase order must have an ordering agency and a provider.",
     "INVOICE_COPY_COUNT_INFO": "Copies received on this invoice: ${0} out of ${1}.",
     "INVOICE_IDENT_COLLIDE": "There is already an invoice in the system with the given combination of 'Vendor Invoice ID' and 'Provider,' which is not allowed.",
-    "NEW_INVOICE": "New Invoice"
+    "NEW_INVOICE": "New Invoice",
+    "NO_LI_GENERAL" : "You have not selected any line items.",
 }
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index b58f612..a541cf2 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -2799,6 +2799,11 @@ function AcqLiTable() {
     this.receiveSelectedLineitems = function() {
         var li_list = this.getSelected();
 
+        if (!li_list.length) {
+            alert(localeStrings.NO_LI_GENERAL);
+            return;
+        }
+
         for (var i = 0; i < li_list.length; i++) {
             var li = li_list[i];
 
@@ -2876,6 +2881,12 @@ function AcqLiTable() {
     };
 
     this.rollbackReceiveLineitems = function() {
+        var li_id_list = this.getSelected(false, null, true);
+        if (!li_id_list.length) {
+            alert(localeStrings.NO_LI_GENERAL);
+            return;
+        }
+
         if (!confirm(localeStrings.ROLLBACK_LI_RECEIVE_CONFIRM)) return;
 
         this.show('acq-lit-progress-numbers');
@@ -2884,7 +2895,7 @@ function AcqLiTable() {
         fieldmapper.standardRequest(
             ['open-ils.acq', 'open-ils.acq.lineitem.receive.rollback.batch'],
             {   async: true,
-                params: [this.authtoken, this.getSelected(false, null, true)],
+                params: [this.authtoken, li_id_list],
                 onresponse : function(r) {
                     var resp = openils.Util.readResponse(r);
                     self._updateProgressNumbers(resp, true);

commit 76d2e15b02e8c65659408e82c3ddfe7744ca24cc
Author: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
Date:   Tue Feb 26 14:28:40 2013 -0500

    Acq: several interface improvements
    
    Several usability improvements throughout Acq, including:
    
      * A logical re-ordering of the main "actions" dropdown in several Acq
        interfaces (selection lists, purchase orders, acq search results, MARC
        federated search interface, etc).
      * "Actions" dropdown also has its options enabled/disabled depending what
        interface you're seeing it through, and redundant entries are
        removed from the per-lineitem secondary dropdown.
      * Add to Purchase Order dialog added to "actions" dropdown
      * Middle layer support for adding many line items to a PO at once
      * Create/add to Purchase Order operations can no longer steal line
        items from current POs
      * Create invoice from / link to invoice now work in new tab
      * Receive/unreceive now by selected lineitem instead of whole PO
      * Claim policy application works more simply now
      * Invoices interface auto-populates "# Invoiced" column with number of
        invoicable copies, and copies the "billed cost" column to the
        "amount paid" column if the latter doesn't have anything in it yet.
      * You can now only cancel specific lineitems when they're actually on
        a PO and have the state of 'on-order'.
      * Avoid double-activation of POs at UI level
      * Disable invoice and cancel options for whole pending POs
      * Disable zero-copy checkbox for activated POs
      * Disable new misc charges (acq.invoice_item) for activated POs
    
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>
    Signed-off-by: Bill Erickson <berick at esilibrary.com>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 75f0e32..1380f85 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -7970,7 +7970,7 @@ SELECT  usr,
 			<field reporter:label="Fund" name="fund" reporter:datatype="link" />
 			<field reporter:label="Fund Debit" name="fund_debit" reporter:datatype="link" />
 			<field reporter:label="Owning Library" name="owning_lib" reporter:datatype="org_unit" />
-			<field reporter:label="Shelving Location" name="location" reporter:datatype="link" />
+			<field reporter:label="Copy Location" name="location" reporter:datatype="link" />
 			<field reporter:label="Circ Modifier" name="circ_modifier" reporter:datatype="link" />
 			<field reporter:label="Note" name="note" reporter:datatype="text" />
 			<field reporter:label="Collection Code" name="collection_code" reporter:datatype="text" />
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
index a86d4e6..ca49a79 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
@@ -580,6 +580,8 @@ sub receive_lineitem {
     my($mgr, $li_id, $skip_complete_check) = @_;
     my $li = $mgr->editor->retrieve_acq_lineitem($li_id) or return 0;
 
+    return 0 unless $li->state eq 'on-order' or $li->state eq 'cancelled'; # sic
+
     my $lid_ids = $mgr->editor->search_acq_lineitem_detail(
         {lineitem => $li_id, recv_time => undef}, {idlist => 1});
 
@@ -1781,6 +1783,14 @@ sub create_purchase_order_api {
                 {flesh => 1, flesh_fields => {jub => ['attributes']}}
             ]) or return $e->die_event;
 
+            return $e->die_event(
+                new OpenILS::Event(
+                    "BAD_PARAMS", payload => $li,
+                        note => "acq.lineitem #" . $li->id .
+                        ": purchase_order #" . $li->purchase_order
+                )
+            ) if $li->purchase_order;
+
             $li->provider($po->provider);
             $li->purchase_order($po->id);
             $li->state('pending-order');
@@ -2146,6 +2156,56 @@ sub receive_lineitem_api {
 
 
 __PACKAGE__->register_method(
+	method => 'receive_lineitem_batch_api',
+	api_name	=> 'open-ils.acq.lineitem.receive.batch',
+	signature => {
+        desc => 'Mark lineitems as received',
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => 'lineitem ID list', type => 'array'}
+        ],
+        return => {desc =>
+            q/On success, stream of objects describing changes to LIs and
+            possibly PO; onerror, Event.  Any event, even after lots of other
+            objects, should mean general failure of whole batch operation./
+        }
+    }
+);
+
+sub receive_lineitem_batch_api {
+    my ($self, $conn, $auth, $li_idlist) = @_;
+
+    return unless ref $li_idlist eq 'ARRAY' and @$li_idlist;
+
+    my $e = new_editor(xact => 1, authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
+
+    my $mgr = new OpenILS::Application::Acq::BatchManager(
+        editor => $e, conn => $conn
+    );
+
+    for my $li_id (map { int $_ } @$li_idlist) {
+        my $li = $e->retrieve_acq_lineitem([
+            $li_id, {
+                flesh => 1,
+                flesh_fields => { jub => ['purchase_order'] }
+            }
+        ]) or return $e->die_event;
+
+        return $e->die_event unless $e->allowed(
+            'RECEIVE_PURCHASE_ORDER', $li->purchase_order->ordering_agency
+        );
+
+        receive_lineitem($mgr, $li_id) or return $e->die_event;
+        $mgr->respond;
+    }
+
+    $e->commit or return $e->die_event;
+    $mgr->respond_complete;
+    $mgr->run_post_response_hooks;
+}
+
+__PACKAGE__->register_method(
     method   => 'rollback_receive_po_api',
     api_name => 'open-ils.acq.purchase_order.receive.rollback'
 );
@@ -2281,6 +2341,65 @@ sub rollback_receive_lineitem_api {
     $e->commit and return $result or return $e->die_event;
 }
 
+__PACKAGE__->register_method(
+    method    => 'rollback_receive_lineitem_batch_api',
+    api_name  => 'open-ils.acq.lineitem.receive.rollback.batch',
+    signature => {
+        desc   => 'Mark a list of lineitems as Un-received',
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => 'lineitem ID list',     type => 'array'}
+        ],
+        return => {desc =>
+            q/on success, a stream of objects describing changes to LI and
+            possibly PO; on error, Event. Any event means all previously
+            returned objects indicate changes that didn't really happen./
+        }
+    }
+);
+
+sub rollback_receive_lineitem_batch_api {
+    my ($self, $conn, $auth, $li_idlist) = @_;
+
+    return unless ref $li_idlist eq 'ARRAY' and @$li_idlist;
+
+    my $e = new_editor(xact => 1, authtoken => $auth);
+    return $e->die_event unless $e->checkauth;
+
+    my $mgr = new OpenILS::Application::Acq::BatchManager(
+        editor => $e, conn => $conn
+    );
+
+    for my $li_id (map { int $_ } @$li_idlist) {
+        my $li = $e->retrieve_acq_lineitem([
+            $li_id, {
+                "flesh" => 1,
+                "flesh_fields" => {"jub" => ["purchase_order"]}
+            }
+        ]);
+
+        my $po = $li->purchase_order;
+
+        return $e->die_event unless
+            $e->allowed('RECEIVE_PURCHASE_ORDER', $po->ordering_agency);
+
+        $li = rollback_receive_lineitem($mgr, $li_id) or return $e->die_event;
+
+        my $result = {"li" => {$li->id => {"state" => $li->state}}};
+        if ($po->state eq "received") { # should happen first time, not after
+            $po->state("on-order");
+            $po = update_purchase_order($mgr, $po) or return $e->die_event;
+        }
+        $result->{"po"} = describe_affected_po($e, $po);
+
+        $mgr->respond(%$result);
+    }
+
+    $e->commit or return $e->die_event;
+    $mgr->respond_complete;
+    $mgr->run_post_response_hooks;
+}
+
 
 __PACKAGE__->register_method(
     method    => 'set_lineitem_price_api',
@@ -2887,16 +3006,12 @@ sub cancel_lineitem {
     # Depending on context, this may not warrant an event.
     return -1 if $li->state eq "cancelled";
 
-    # But this always does.
+    # But this always does.  Note that this used to be looser, but you can
+    # no longer cancel lineitems that lack a PO or that are in "pending-order"
+    # state (you could in the past).
     return new OpenILS::Event(
         "ACQ_NOT_CANCELABLE", "note" => "lineitem $li_id"
-    ) unless (
-        (! $li->purchase_order) or (
-            $li->purchase_order and (
-                $li->state eq "on-order" or $li->state eq "pending-order"
-            )
-        )
-    );
+    ) unless $li->purchase_order and $li->state eq "on-order";
 
     $li->state("cancelled");
     $li->cancel_reason($cancel_reason->id);
@@ -3517,7 +3632,7 @@ __PACKAGE__->register_method(
         params => [
             {desc => 'Authentication token', type => 'string'},
             {desc => 'The purchase order id', type => 'number'},
-            {desc => 'The lineitem ID', type => 'number'},
+            {desc => 'The lineitem ID (or an array of them)', type => 'mixed'},
         ],
         return => {desc => 'Streams a total versus completed counts object, event on error'}
     }
@@ -3534,9 +3649,6 @@ sub add_li_to_po {
     my $po = $e->retrieve_acq_purchase_order($po_id)
         or return $e->die_event;
 
-    my $li = $e->retrieve_acq_lineitem($li_id)
-        or return $e->die_event;
-
     return $e->die_event unless 
         $e->allowed('CREATE_PURCHASE_ORDER', $po->ordering_agency);
 
@@ -3545,16 +3657,33 @@ sub add_li_to_po {
         return {success => 0, po => $po, error => 'bad-po-state'};
     }
 
-    unless ($li->state =~ /new|order-ready|pending-order/) {
-        $e->rollback;
-        return {success => 0, li => $li, error => 'bad-li-state'};
+    my $lis;
+
+    if (ref $li_id eq "ARRAY") {
+        $li_id = [ map { int($_) } @$li_id ];
+        return $e->die_event(new OpenILS::Event("BAD_PARAMS")) unless @$li_id;
+
+        $lis = $e->search_acq_lineitem({id => $li_id})
+            or return $e->die_event;
+    } else {
+        my $li = $e->retrieve_acq_lineitem(int($li_id))
+            or return $e->die_event;
+        $lis = [$li];
+    }
+
+    foreach my $li (@$lis) {
+        if ($li->state !~ /new|order-ready|pending-order/ or
+            $li->purchase_order) {
+            $e->rollback;
+            return {success => 0, li => $li, error => 'bad-li-state'};
+        }
+
+        $li->provider($po->provider);
+        $li->purchase_order($po_id);
+        $li->state('pending-order');
+        update_lineitem($mgr, $li) or return $e->die_event;
     }
 
-    $li->provider($po->provider);
-    $li->purchase_order($po_id);
-    $li->state('pending-order');
-    update_lineitem($mgr, $li) or return $e->die_event;
-    
     $e->commit;
     return {success => 1};
 }
diff --git a/Open-ILS/src/templates/acq/common/add_to_po.tt2 b/Open-ILS/src/templates/acq/common/add_to_po.tt2
new file mode 100644
index 0000000..370bd28
--- /dev/null
+++ b/Open-ILS/src/templates/acq/common/add_to_po.tt2
@@ -0,0 +1,57 @@
+        <div dojoType="dijit.Dialog" jsId='addToPoDialog'>
+            <script type="dojo/connect" event="execute">
+                var dia = this;
+                var poId = addToPoInput.attr('value');
+                if (!poId) return false;
+
+                var liId = dia._get_li();
+                console.log("adding li " + liId + " to PO " + poId);
+
+                fieldmapper.standardRequest(
+                    ['open-ils.acq',
+                        'open-ils.acq.purchase_order.add_lineitem'],
+                    {   async: true,
+                        params: [openils.User.authtoken, poId, liId],
+                        oncomplete: function(r) {
+                            if ((r = openils.Util.readResponse(r))) {
+                                if (r.success) {
+                                    location.href = oilsBasePath +
+                                        '/acq/po/view/' + poId;
+                                } else {
+                                    if (r.error == 'bad-po-state') {
+                                        alert(localeStrings.ADD_LI_TO_PO_BAD_PO_STATE);
+                                    } else if (r.error == 'bad-li-state') {
+                                        alert(localeStrings.ADD_LI_TO_PO_BAD_LI_STATE);
+                                    }
+                                }
+                            }
+                            dia.hide();
+                        }
+                    }
+                );
+            </script>
+            <script type="dojo/connect" event="onShow">
+                if (!window._already_addToPo_onShow) {
+                    var dia = this;
+                    openils.Util.registerEnterHandler(
+                        addToPoInput.domNode,
+                        function() { dia.execute(); }
+                    );
+                    window._already_addToPo_onShow = true;
+                }
+            </script>
+            <table class='dijitTooltipTable'>
+                <tr>
+                    <td><label>[% l('Enter the PO Name: ') %]</label></td>
+                    <td>
+                        <input jsId="addToPoInput" dojoType="openils.widget.PCrudAutocompleteBox" fmclass="acqpo" searchAttr="name" name="target_po" />
+
+                        </td>
+                </tr>
+                <tr>
+                    <td colspan='2' align='center'>
+                        <span dojoType='dijit.form.Button' type="submit">[% l('Save') %]</span>
+                    </td>
+                </tr>
+            </table>
+        </div>
diff --git a/Open-ILS/src/templates/acq/common/li_table.tt2 b/Open-ILS/src/templates/acq/common/li_table.tt2
index 8e1c209..064ff52 100644
--- a/Open-ILS/src/templates/acq/common/li_table.tt2
+++ b/Open-ILS/src/templates/acq/common/li_table.tt2
@@ -5,7 +5,7 @@
 <div id='acq-lit-table-container'>
     <div id='acq-lit-table-div' class='hidden'>
 
-        <!-- Lineitem (bib record) list -->
+        <!-- Line Item (bib record) list -->
         <table id='acq-lit-table' class='oils-generic-table'>
             <thead>
                 <tr>
@@ -15,25 +15,36 @@
                                 <td>
                                     <span>
                                         <select id="acq-lit-li-actions-selector">
+                                    <!-- mask meanings:
+                                        pl: selection list
+                                        po: pending purchase order
+                                        ao: activated purchase order
+                                        gs: general search
+                                        vp: view/place orders
+                                        fs: MARC federated search
+
+                                        * for all, otherwise combine with |
+                                    -->
                                             <option mask='*'  value='_'>[% l('--Actions--') %]</option>
-                                            <option mask='sr' value='save_picklist'>[% l('Save Items To Selection List') %]</option>
-                                            <option mask='pl' value='selector_ready'>[% l('Mark Ready for Selector') %]</option>
-                                            <option mask='pl' value='order_ready'>[% l('Mark Ready for Order') %]</option>
-                                            <option mask='*'  value='delete_selected'>[% l('Delete Selected Items') %]</option>
-                                            <option mask='*'  value='add_brief_record'>[% l('Add Brief Record') %]</option>
-                                            <option mask='*'  value='export_attr_list'>[% l('Export Single Attribute List') %]</option>
-                                            <option mask='*'  value='batch_apply_funds'>[% l('Apply Funds to Selected Items') %]</option>
-                                            <option mask='po' value='' disabled='disabled'>[% l('----PO----') %]</option>
-                                            <option mask='sr|pl' value='create_order'>[% l('Create Purchase Order') %]</option>
+                                            <option mask='pl|gs|vp|fs' value='save_picklist'>[% l('Save Items To Selection List') %]</option>
+                                            <option mask='pl|gs|vp' value='selector_ready'>[% l('Mark Ready for Selector') %]</option>
+                                            <option mask='pl|gs|vp' value='order_ready'>[% l('Mark Ready for Order') %]</option>
+                                            <option mask='pl|po|gs|vp'  value='delete_selected'>[% l('Delete Selected Items') %]</option>
+                                            <option mask='pl|po'  value='add_brief_record'>[% l('Add Brief Record') %]</option>
+                                            <option mask='pl|po|ao|gs'  value='export_attr_list'>[% l('Export Single Attribute List') %]</option>
+                                            <option mask='*' value='' disabled='disabled'>[% l('----PO----') %]</option>
+                                            <option mask='pl|gs|vp|fs' value='create_order'>[% l('Create Purchase Order') %]</option>
+                                            <option mask='pl|gs|vp|fs' value='add_to_order'>[% l('Add to Purchase Order') %]</option>
+                                            <option mask='po|ao' value='print_po'>[% l('Print Purchase Order') %]</option>
+                                            <option mask='po|ao' value='po_history'>[% l('View PO History') %]</option>
                                             <option mask='po' value='create_assets'>[% l('Load Bibs and Items') %]</option>
-                                            <option mask='po' value='cancel_lineitems'>[% l('Cancel Selected Lineitems') %]</option>
-                                            <option mask='po' value='change_claim_policy'>[% l('Change Claim Policy') %]</option>
-                                            <option mask='po' value='receive_po' id='receive_po' disabled='disabled'>[% l('Mark Purchase Order as Received') %]</option>
-                                            <option mask='po' value='rollback_receive_po' id='rollback_receive_po' disabled='disabled'>[% l('Un-Receive Purchase Order') %]</option>
-                                            <option mask='po' value='print_po'>[% l('Print Purchase Order') %]</option>
-                                            <option mask='po' value='po_history'>[% l('View PO History') %]</option>
-                                            <option mask='po' value='batch_create_invoice'>[% l('Create Invoice From Selected Lineitems') %]</option>
-                                            <option mask='po' value='batch_link_invoice'>[% l('Link To Invoice for Selected Lineitems') %]</option>
+                                            <!-- <option mask=''  value='batch_apply_funds'>[% l('Apply Funds to Selected Items') %]</option> XXX moving to batch updater -->
+                                            <option mask='ao|gs|vp' value='cancel_lineitems'>[% l('Cancel Selected Line Items') %]</option>
+                                            <option mask='po|ao|gs|vp' value='apply_claim_policy'>[% l('Apply Claim Policy to Selected Line Items') %]</option><!-- can the functionality desired here be covered by the next thing? -->
+                                            <option mask='ao|gs|vp' value='receive_lineitems' id='receive_lineitems' disabled='disabled'>[% l('Mark Selected Line Items as Received') %]</option>
+                                            <option mask='ao|gs|vp' value='rollback_receive_lineitems' id='rollback_receive_lineitems' disabled='disabled'>[% l('Un-Receive Selected Line Items') %]</option>
+                                            <option mask='ao|gs|vp' value='batch_create_invoice'>[% l('Create Invoice From Selected Line Items') %]</option>
+                                            <option mask='ao|gs|vp' value='batch_link_invoice'>[% l('Link Selected Line Items to Invoice') %]</option>
                                         </select>
                                         <span id="acq-lit-export-attr-holder" class="hidden">
                                             <input dojoType="dijit.form.FilteringSelect" id="acq-lit-export-attr" jsId="acqLitExportAttrSelector" labelAttr="description" searchAttr="description" />
@@ -136,6 +147,8 @@
                                         <span name='pl' class='hidden'> | <a title='[% l('Select List') %]' name='pl_link' href='javascript:void(0);'>&#x2756; </a></span>
                                         <span name='po' class='hidden'> | <a title='[% l('Purchase Order') %]' name='po_link' href='javascript:void(0);'>&#x2318; </a></span>
                                         <span name="show_requests"> | <a title='[% l('Patron Requests') %]' name="show_requests_link" href="javascript:void(0);">[% l('requests') %]</a></span> 
+                                        <span name="invoices_span" class="hidden"> | <a href="javascript:void(0);" name="invoices_link">[% l("view invoice(s)") %] </a></span>
+                                        <span name="claim_policy" class="hidden"> | [% l("claim policy:") %] <span name="claim_policy_name"></span></span>
                                         <span name='pro' class='hidden'> | <a title='[% l('Provider') %]' name='pro_link' href='javascript:void(0);'>&#x235F; </a></span>
                                         <span name='queue' class='hidden'> | <a title='[% l('Import Queue') %]' name='queue_link' href='javascript:void(0);'>[% l('&#x27AC; queue') %]</a></span>
                                     </td>
@@ -154,14 +167,8 @@
                     <td>
                         <select name='actions'>
                             <option name='action_none'>[% l('-- Actions --') %]</option>
-                            <option name='action_mark_recv' disabled='disabled'>[% l('Mark Received') %]</option>
-                            <option name='action_mark_unrecv' disabled='disabled'>[% l('Un-Receive') %]</option>
                             <option name='action_update_barcodes'>[% l('Update Barcodes') %]</option>
                             <option name='action_holdings_maint'>[% l('Holdings Maint.') %]</option>
-                            <option name='action_new_invoice' disabled='disabled'>[% l('New Invoice') %]</option>
-                            <option name='action_link_invoice' disabled='disabled'>[% l('Link to Invoice') %]</option>
-                            <option name='action_view_invoice' disabled='disabled'>[% l('View Invoice(s)') %]</option>
-                            <option name='action_view_claim_policy'>[% l('Apply Claim Policy') %]</option>
                             <option name='action_manage_claims'>[% l('Claims') %]</option>
                             <option name='action_view_history'>[% l('View History') %]</option>
                         </select>
@@ -206,10 +213,10 @@
         </table>
     </div>
 
-    <!-- Bib record / Lineitem info table -->
+    <!-- Bib record / Line Item info table -->
     [% INCLUDE "acq/common/info.tt2" which = "Lit" %]
 
-    <!-- Lineitem notes table -->
+    <!-- Line Item notes table -->
     [% INCLUDE "acq/common/notes.tt2" which = "Lit" %]
 
     <!-- Copies table -->
@@ -276,7 +283,7 @@
             <tbody style='font-weight:bold;'>
                 <tr>
                     <td style='margin-top:30px;'>[% l('Owning Branch') %]</td>
-                    <td>[% l('Shelving Location') %]</td>
+                    <td>[% l('Copy Location') %]</td>
                     <td>[% l('Collection Code') %]</td>
                     <td>[% l('Fund') %]</td>
                     <td>[% l('Circ Modifier') %]</td>
@@ -303,7 +310,7 @@
             <tbody style='font-weight:bold;'>
                 <tr>
                     <td style='margin-top:30px;'>[% l('Owning Branch') %]</td>
-                    <td>[% l('Shelving Location') %]</td>
+                    <td>[% l('Copy Location') %]</td>
                     <td>[% l('Collection Code') %]</td>
                     <td>[% l('Fund') %]</td>
                     <td>[% l('Circ Modifier') %]</td>
@@ -356,7 +363,7 @@
             <tbody style='font-weight:bold;'>
                 <tr>
                     <td style='margin-top:30px;'>[% l('Owning Branch') %]</td>
-                    <td>[% l('Shelving Location') %]</td>
+                    <td>[% l('Copy Location') %]</td>
                     <td>[% l('Circ Modifier') %]</td>
                     <td>[% l('Callnumber') %]</td>
                     <td>[% l('Barcode') %]</td>
@@ -379,6 +386,7 @@
         <div jsId="acqLitLinkInvoiceDialog" dojoType="dijit.Dialog">
             [% INCLUDE "acq/common/inv_dialog.tt2" which = "li" %]
         </div>
+        [% INCLUDE "acq/common/add_to_po.tt2" %]
     </div>
 
 
@@ -418,11 +426,11 @@
                     <td><input id="acq-lit-po-prepay" name="prepayment_required" dojoType="dijit.form.CheckBox"/></td>
                 </tr>
                 <tr>
-                    <td>[% l('All Lineitems') %]</td>
+                    <td>[% l('All Line Items') %]</td>
                     <td><input checked='checked' name='create_from' value='all' dojoType='dijit.form.RadioButton'/></td>
                 </tr>
                 <tr>
-                    <td>[% l('Selected Lineitems') %]</td>
+                    <td>[% l('Selected Line Items') %]</td>
                     <td><input name='create_from' value='selected' dojoType='dijit.form.RadioButton'/></td>
                 </tr>
                 <tr>
diff --git a/Open-ILS/src/templates/acq/invoice/receive.tt2 b/Open-ILS/src/templates/acq/invoice/receive.tt2
index 1a964a6..80756cf 100644
--- a/Open-ILS/src/templates/acq/invoice/receive.tt2
+++ b/Open-ILS/src/templates/acq/invoice/receive.tt2
@@ -39,7 +39,7 @@
                             /><label for="select_all">[% l(' Select All') %]</label>
                     </th>
                     <th>[% l('Owning Branch') %]</th>
-                    <th>[% l('Shelving Location') %]</th>
+                    <th>[% l('Copy Location') %]</th>
                     <th>[% l('Collection Code') %]</th>
                     <th>[% l('Fund') %]</th>
                     <th>[% l('Circ Modifier') %]</th>
diff --git a/Open-ILS/src/templates/acq/invoice/view.tt2 b/Open-ILS/src/templates/acq/invoice/view.tt2
index 3871c64..678eec5 100644
--- a/Open-ILS/src/templates/acq/invoice/view.tt2
+++ b/Open-ILS/src/templates/acq/invoice/view.tt2
@@ -73,9 +73,9 @@
                     <thead id='acq-invoice-entry-thead' class='hidden'>
                         <tr>
                             <th colspan='2'>[% l('Title Details') %]</th>
-                            <th class='acq-invoice-center-col'>[% l('#&nbsp;Invoiced&nbsp;/&nbsp;#&nbsp;Paid') %]</th>
+                            <th class='acq-invoice-center-col'>[% l('# Invoiced / # Paid') %]</th>
                             <th class='acq-invoice-center-col'>[% l('Billed') %]</th>
-                            <th class='acq-invoice-paid-per-copy-col'>[% l('Per Copy') %]</th>
+                            <th class='acq-invoice-paid-per-copy-col' style="white-space: normal">[% l('Per Copy') %]</th>
                             <th class='acq-invoice-paid-col'>[% l('Paid') %]</th>
                             <th class='acq-invoice-center-col hide-complete'>[% l('Detach') %]</th>
                         </tr>
@@ -103,11 +103,11 @@
                         </tr>
                     </tbody>
                     <!-- acq.invoice_item -->
-                    <thead>
+                    <thead id="acq-invoice-item-thead">
                         <tr>
                             <th>[% l('Charge Type') %]</th>
                             <th class='acq-invoice-center-col'>[% l('Fund') %]</th>
-                            <th>[% l('Title/Description') %]</th>
+                            <th>[% l('Title / Description') %]</th>
                             <th class='acq-invoice-center-col'>[% l('Billed') %]</th>
                             <th/>
                             <th class='acq-invoice-paid-col'>[% l('Paid') %]</th>
diff --git a/Open-ILS/src/templates/acq/lineitem/related.tt2 b/Open-ILS/src/templates/acq/lineitem/related.tt2
index fe2f527..337a64f 100644
--- a/Open-ILS/src/templates/acq/lineitem/related.tt2
+++ b/Open-ILS/src/templates/acq/lineitem/related.tt2
@@ -49,23 +49,12 @@
     </div>
 
     <div class="hidden">
-        <div dojoType="dijit.Dialog" jsId='addToPoDialog'>
-            <table class='dijitTooltipTable'>
-                <tr>
-                    <td><label>[% l('Enter the PO #: ') %]</label></td>
-                    <td><input jsId='addToPoInput' dojoType="dijit.form.TextBox" /></td>
-                </tr>
-                <tr>
-                    <td colspan='2' align='center'>
-                        <button dojoType='dijit.form.Button' jsId='addToPoSave' type="submit">[% l('Save') %]</button>
-                    </td>
-                </tr>
-            </table>
-        </div>
+        [% INCLUDE "acq/common/add_to_po.tt2" %]
     </div>
 
 </div>
 [% INCLUDE "acq/common/info.tt2" which = "Related" %]
 [% INCLUDE "acq/common/li_table.tt2" %]
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/common/add_to_po.js"></script>
 <script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/lineitem/related.js"></script>
 [% END %]
diff --git a/Open-ILS/src/templates/acq/po/item_table.tt2 b/Open-ILS/src/templates/acq/po/item_table.tt2
index 80497c5..be61c8e 100644
--- a/Open-ILS/src/templates/acq/po/item_table.tt2
+++ b/Open-ILS/src/templates/acq/po/item_table.tt2
@@ -27,7 +27,7 @@
     <div id="acq-po-item-table-i-am-empty" class="hidden">
         <em>[% l('There are no miscellanea attached to this purchase order.') %]</em>
     </div>
-    <div id="acq-po-item-table-controls">
+    <div id="acq-po-item-table-controls" class="hidden">
         <button id="acq-po-item-table-new-charge">[% l('New Charge') %]</button> &nbsp;
         <button id="acq-po-item-table-save-new">[% l('Save New Charges') %]</button>
     </div>
diff --git a/Open-ILS/src/templates/acq/po/view.tt2 b/Open-ILS/src/templates/acq/po/view.tt2
index 2bc08e6..143a9d2 100644
--- a/Open-ILS/src/templates/acq/po/view.tt2
+++ b/Open-ILS/src/templates/acq/po/view.tt2
@@ -21,7 +21,7 @@
                     <th>[% l('Activatable?') %]</th>
                     <td>
                         <span id="acq-po-activate-checking"></span>
-                        <a class="hidden" id="acq-po-activate-link" href="javascript:void(0);" onclick="activatePo()">[% l('Activate Order') %]</a>
+                        <span class="hidden" id="acq-po-activate-link"><span dojoType="dijit.form.Button" onClick="activatePo()" jsId="activatePoButton">[% l('Activate Order') %]</span></span>
                     </td>
                 </tr>
                 <tr>
@@ -54,7 +54,7 @@
                 <tr>
                     <th>[% l('Total Encumbered') %]</th>
                     <td>[% l('$[_1]', '<span id="acq-po-view-total-enc"></span>') %]</td>
-                    <th>[% l('Invoicing') %]</th>
+                    <th id="acq-po-invoice-label" class="hidden">[% l('Invoicing') %]</th>
                     <td id="acq-po-invoice-stuff" class="hidden">
                         <button dojoType="dijit.form.Button"
                             id="acq-po-view-invoice-link">
@@ -115,8 +115,8 @@
                             <button dojoType='dijit.form.Button' id='acq-po-return-to-invoice-button'>[% l('&#x2196; Return to Invoice') %]</button>
                         </div>
                     </td>
-                    <th>[% l('Allow activation with <br/> zero-copy lineitems') %]</th>
-                    <td><input type='checkbox' id='acq-po-activate-zero-copies' onclick='checkCouldActivatePo()'/></td>
+                    <th class="hidden" id="acq-po-zero-activate-label">[% l('Allow activation with <br/> zero-copy lineitems') %]</th>
+                    <td class="hidden" id="acq-po-zero-activate"><input type='checkbox' id='acq-po-activate-zero-copies' onclick='checkCouldActivatePo()'/></td>
                 </tr>
                 <tr>
                     <td colspan='3'>
diff --git a/Open-ILS/src/templates/conify/global/acq/distribution_formula.tt2 b/Open-ILS/src/templates/conify/global/acq/distribution_formula.tt2
index e3dfd1a..d80e29e 100644
--- a/Open-ILS/src/templates/conify/global/acq/distribution_formula.tt2
+++ b/Open-ILS/src/templates/conify/global/acq/distribution_formula.tt2
@@ -49,7 +49,7 @@
             <tr>
                 <th></th>
                 <th>[% l('Owning Library') %]</th>
-                <th>[% l('Shelving Location') %]</th>
+                <th>[% l('Copy Location') %]</th>
                 <th>[% l('Item Count') %]</th>
                 <th></th>
             </tr>
diff --git a/Open-ILS/src/templates/conify/global/acq/provider.tt2 b/Open-ILS/src/templates/conify/global/acq/provider.tt2
index 7040320..1eeafab 100644
--- a/Open-ILS/src/templates/conify/global/acq/provider.tt2
+++ b/Open-ILS/src/templates/conify/global/acq/provider.tt2
@@ -153,7 +153,7 @@
                     <option value='fund_code'>[% l('Fund Code') %]</option>
                     <option value='circ_modifier'>[% l('Circ Modifier') %]</option>
                     <option value='note'>[% l('Note') %]</option>
-                    <option value='copy_location'>[% l('Shelving Location') %]</option>
+                    <option value='copy_location'>[% l('Copy Location') %]</option>
                     <option value='barcode'>[% l('Barcode') %]</option>
                     <option value='collection_code'>[% l('Collection Code') %]</option>
                 </select>
diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css
index e881e26..cfa600d 100644
--- a/Open-ILS/web/css/skin/default/acq.css
+++ b/Open-ILS/web/css/skin/default/acq.css
@@ -216,6 +216,8 @@ span[name="notes_alert_flag"] {color: #c00;font-weight: bold;font-size: 110%;mar
 .acq-invoice-paid-col {background : #E0E0E0; text-align: center;}
 .acq-invoice-center-col { text-align: center; }
 .acq-invoice-money { width: 7em; }
+#acq-invoice-entry-thead th { white-space: nowrap; }
+#acq-invoice-item-thead th { white-space: nowrap; }
 
 .acq-lineitem-summary { font-weight: bold; }
 .acq-lineitem-summary-extra { padding-left: 10px; }
diff --git a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
index fd7949f..ad82f7b 100644
--- a/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
+++ b/Open-ILS/web/js/dojo/openils/acq/nls/acq.js
@@ -1,6 +1,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?",
+    "ROLLBACK_LI_RECEIVE_CONFIRM" : "This will rollback receipt of selected line items from 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?",
     "NO_PO_RESULTS" : "No results",
@@ -87,5 +88,6 @@
     "COPIES_TO_RECEIVE": "Number of copies to receive: ",
     "CREATE_PO_INVALID": "A purchase order must have an ordering agency and a provider.",
     "INVOICE_COPY_COUNT_INFO": "Copies received on this invoice: ${0} out of ${1}.",
-    "INVOICE_IDENT_COLLIDE": "There is already an invoice in the system with the given combination of 'Vendor Invoice ID' and 'Provider,' which is not allowed."
+    "INVOICE_IDENT_COLLIDE": "There is already an invoice in the system with the given combination of 'Vendor Invoice ID' and 'Provider,' which is not allowed.",
+    "NEW_INVOICE": "New Invoice"
 }
diff --git a/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js b/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
index 7a5789e..6ef5126 100644
--- a/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
+++ b/Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
@@ -11,7 +11,18 @@ function InvoiceLinkDialogManager(which, target) {
             var join = (idx == 0) ? '?' : '&';
             path += join + "attach_" + self.which + "=" + id;
         });
-        location.href = path;
+        if (openils.XUL.isXUL()) {
+            openils.XUL.newTabEasy(
+                path,
+                /* tab title */ dojo.string.substitute(
+                    localeStrings.INVOICE_NUMBER, [self.inv.inv_ident()]
+                ),
+                null,
+                true /* <browser> wrapper */
+            );
+        } else {
+            location.href = path;
+        }
     };
 
     this.which = which;
diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js
index d1cfe99..b58f612 100644
--- a/Open-ILS/web/js/ui/default/acq/common/li_table.js
+++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js
@@ -6,6 +6,7 @@ dojo.require('dijit.form.FilteringSelect');
 dojo.require('dijit.form.Textarea');
 dojo.require('dijit.Tooltip');
 dojo.require('dijit.ProgressBar');
+dojo.require('dojox.timing.doLater');
 dojo.require('openils.acq.Lineitem');
 dojo.require('openils.acq.PO');
 dojo.require('openils.acq.Picklist');
@@ -173,6 +174,28 @@ function AcqLiTable() {
         }
     };
 
+    this.enableActionsDropdownOptions = function(mask) {
+        /* 'mask' is probably a minomer the way I'm using it, but it needs to
+         * be one of pl,po,ao,gs,vp, or fs. */
+        dojo.query("option", "acq-lit-li-actions-selector").forEach(
+            function(option) {
+                var opt_mask = dojo.attr(option, "mask");
+
+                /* For each <option> element, an empty or non-existent mask
+                 * attribute, a mask attribute of "*", or a mask attribute that
+                 * matches this method's argument should result in that
+                 * option's being enabled. */
+                dojo.attr(
+                    option, "disabled", !(
+                        !opt_mask ||
+                        opt_mask == "*" ||
+                        opt_mask.search(mask) != -1
+                    )
+                );
+            }
+        );
+    };
+
     /*
      * Ensures this.focusLineitem is in view and causes a brief 
      * border around the lineitem to come to life then fade.
@@ -333,38 +356,50 @@ function AcqLiTable() {
     };
 
     this.setClaimPolicyControl = function(li, row) {
-        if (!self.claimPolicyPicker) {
-            self.claimPolicyPicker = true; /* prevents a race condition */
+        if (!self._claimPolicyPickerLoading) {
+            self._claimPolicyPickerLoading = true;
+
             new openils.widget.AutoFieldWidget({
                 "parentNode": "acq-lit-li-claim-policy",
                 "fmClass": "acqclp",
                 "selfReference": true,
                 "dijitArgs": {"required": true}
-            }).build(function(w) { self.claimPolicyPicker = w; });
+            }).build(
+                function(w) { self.claimPolicyPicker = w; }
+            );
         }
 
-        if (!row) row = this._findLiRow(li);
-
-        var actViewPolicy = nodeByName("action_view_claim_policy", row);
-        if (li.claim_policy())
-            actViewPolicy.innerHTML = localeStrings.CHANGE_CLAIM_POLICY;
-
-        if (!actViewPolicy.onclick) {
-            actViewPolicy.onclick = function() {
-                if (li.claim_policy())
-                    self.claimPolicyPicker.attr("value", li.claim_policy());
-                liClaimPolicyDialog.show();
-                liClaimPolicySave.onClick = function() {
-                    self.changeClaimPolicy(
-                        [li], self.claimPolicyPicker.attr("value"),
-                        function() {
-                            self.setClaimPolicyControl(li, row);
-                            self.reconsiderClaimControl(li, row);
-                            liClaimPolicyDialog.hide();
-                        }
-                    );
-                }
-            };
+        /* dojox.timing.doLater() is the best thing ever. Resource not yet
+         * ready? Just repeat my whole method when it is. */
+        if (dojox.timing.doLater(self.claimPolicyPicker)) {
+            return;
+        } else {
+            if (!row)
+                row = self._findLiRow(li);
+
+            if (li.claim_policy()) {
+                /* This Dojo data dance is necessary to get a whole fieldmapper
+                 * object based on a claim policy ID, since we alreay have the
+                 * widget thing loaded with all that data, and can thereby
+                 * avoid another request to the server. */
+                self.claimPolicyPicker.store.fetchItemByIdentity({
+                    "identity": li.claim_policy(),
+                    "onItem": function(a) {
+                        var policy = (new acqclp()).fromStoreItem(a);
+                        var span = nodeByName("claim_policy", row);
+                        var inner = nodeByName("claim_policy_name", row);
+
+                        openils.Util.show(span, "inline");
+                        inner.innerHTML = policy.name();
+                    },
+                    "onError": function(e) {
+                        console.error(e);
+                    }
+                });
+            } else {
+                openils.Util.hide(nodeByName("claim_policy", row));
+                nodeByName("claim_policy_name", row).innerHTML = "";
+            }
         }
     };
 
@@ -864,13 +899,8 @@ function AcqLiTable() {
     this.updateLiState = function(li, row) {
         if (!row) row = this._findLiRow(li);
 
-        var actReceive = nodeByName("action_mark_recv", row);
-        var actUnRecv = nodeByName("action_mark_unrecv", row);
         var actUpdateBarcodes = nodeByName("action_update_barcodes", row);
         var actHoldingsMaint = nodeByName("action_holdings_maint", row);
-        var actNewInvoice = nodeByName('action_new_invoice', row);
-        var actLinkInvoice = nodeByName('action_link_invoice', row);
-        var actViewInvoice = nodeByName('action_view_invoice', row);
 
         // always allow access to LI history
         nodeByName('action_view_history', row).onclick = 
@@ -881,33 +911,15 @@ function AcqLiTable() {
         openils.Util.addCSSClass(row, "oils-acq-li-state-" + li.state());
 
         // Expose invoice actions for any lineitem that is linked to a PO 
-        if( li.purchase_order() ) {
-
-            actNewInvoice.disabled = false;
-            actLinkInvoice.disabled = false;
-            actViewInvoice.disabled = false;
-
-            actNewInvoice.onclick = function() {
-                location.href = oilsBasePath + '/acq/invoice/view?create=1&attach_li=' + li.id();
-                nodeByName("action_none", row).selected = true;
-            };
-
-            actLinkInvoice.onclick = function() {
-                if (!self.invoiceLinkDialogManager) {
-                    self.invoiceLinkDialogManager =
-                        new InvoiceLinkDialogManager("li");
-                }
-                self.invoiceLinkDialogManager.target = li;
-                acqLitLinkInvoiceDialog.show();
-                nodeByName("action_none", row).selected = true;
-            };
-
-            actViewInvoice.onclick = function() {
-                location.href = oilsBasePath +
-                    "/acq/search/unified?so=" +
-                    base64Encode({"jub":[{"id": li.id()}]}) +
-                    "&rt=invoice";
-                nodeByName("action_none", row).selected = true;
+        if (li.purchase_order()) {
+            openils.Util.show(nodeByName("invoices_span", row), "inline");
+            var link = nodeByName("invoices_link", row);
+            link.onclick = function() {
+                openils.XUL.newTabEasy(
+                    oilsBasePath + "/acq/search/unified?so=" +
+                    base64Encode({"jub":[{"id": li.id()}]}) + "&rt=invoice"
+                );
+                return false;
             };
         }
                 
@@ -954,21 +966,9 @@ function AcqLiTable() {
                 return; // all done
 
             case "on-order":
-                actReceive.disabled = false;
-                actReceive.onclick = function() {
-                    if (self.checkLiAlerts(li.id()))
-                        self.issueReceive(li);
-                    nodeByName("action_none", row).selected = true;
-                };
                 break;
 
             case "received":
-                actUnRecv.disabled = false;
-                actUnRecv.onclick = function() {
-                    if (confirm(localeStrings.UNRECEIVE_LI))
-                        self.issueReceive(li, /* rollback */ true);
-                    nodeByName("action_none", row).selected = true;
-                };
                 break;
         }
 
@@ -2486,6 +2486,14 @@ function AcqLiTable() {
                 this._deleteLiList(self.getSelected());
                 break;
 
+            case 'add_to_order':
+                addToPoDialog._get_li = dojo.hitch(
+                    this,
+                    function() { return this.getSelected(false, null, true); }
+                );
+                addToPoDialog.show();
+                break;
+
             case 'create_order':
                 this._loadPOSelect();
                 acqLitPoCreateDialog.show();
@@ -2517,12 +2525,12 @@ function AcqLiTable() {
                 this.batchLinkInvoice();
                 break;
 
-            case 'receive_po':
-                this.receivePO();
+            case 'receive_lineitems':
+                this.receiveSelectedLineitems();
                 break;
 
-            case 'rollback_receive_po':
-                this.rollbackPoReceive();
+            case 'rollback_receive_lineitems':
+                this.rollbackReceiveLineitems();
                 break;
 
             case 'create_assets':
@@ -2549,7 +2557,7 @@ function AcqLiTable() {
                 this.maybeCancelLineitems();
                 break;
 
-            case "change_claim_policy":
+            case "apply_claim_policy":
                 var li_list = this.getSelected();
                 this.claimPolicyPicker.attr("value", null);
                 liClaimPolicyDialog.show();
@@ -2771,7 +2779,10 @@ function AcqLiTable() {
         if (!liIds.length) return;
         var path = oilsBasePath + '/acq/invoice/view?create=1';
         dojo.forEach(liIds, function(li, idx) { path += '&attach_li=' + li });
-        location.href = path;
+        if (openils.XUL.isXUL())
+            openils.XUL.newTabEasy(path, localeStrings.NEW_INVOICE, null, true);
+        else
+            location.href = path;
     };
 
     this.batchLinkInvoice = function(create) {
@@ -2785,29 +2796,33 @@ function AcqLiTable() {
         acqLitLinkInvoiceDialog.show();
     };
 
-    this.receivePO = function() {
-        if (!this.isPO) return;
+    this.receiveSelectedLineitems = function() {
+        var li_list = this.getSelected();
+
+        for (var i = 0; i < li_list.length; i++) {
+            var li = li_list[i];
 
-        for (var id in this.liCache) {
-            /* assumption: liCache reflects exactly the
-             * set of LIs that belong to our PO */
-            if (this.liCache[id].state() != "received" &&
-                !this.checkLiAlerts(id)) return;
+            if (li.state() != "received" &&
+                !this.checkLiAlerts(li.id())) return;
         }
 
         this.show('acq-lit-progress-numbers');
+
         var self = this;
         fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.receive'],
+            ['open-ils.acq', 'open-ils.acq.lineitem.receive.batch'],
             {   async: true,
-                params: [this.authtoken, this.isPO],
+                params: [
+                    this.authtoken,
+                    li_list.map(function(li) { return li.id(); })
+                ],
                 onresponse : function(r) {
                     var resp = openils.Util.readResponse(r);
                     self._updateProgressNumbers(resp, true);
                 },
             }
         );
-    }
+    };
 
     this.issueReceive = function(obj, rollback) {
         var part =
@@ -2860,22 +2875,23 @@ function AcqLiTable() {
         }
     };
 
-    this.rollbackPoReceive = function() {
-        if(!this.isPO) return;
-        if(!confirm(localeStrings.ROLLBACK_PO_RECEIVE_CONFIRM)) return;
+    this.rollbackReceiveLineitems = function() {
+        if (!confirm(localeStrings.ROLLBACK_LI_RECEIVE_CONFIRM)) return;
+
         this.show('acq-lit-progress-numbers');
         var self = this;
+
         fieldmapper.standardRequest(
-            ['open-ils.acq', 'open-ils.acq.purchase_order.receive.rollback'],
+            ['open-ils.acq', 'open-ils.acq.lineitem.receive.rollback.batch'],
             {   async: true,
-                params: [this.authtoken, this.isPO],
+                params: [this.authtoken, this.getSelected(false, null, true)],
                 onresponse : function(r) {
                     var resp = openils.Util.readResponse(r);
                     self._updateProgressNumbers(resp, true);
                 },
             }
         );
-    }
+    };
 
     this._updateProgressNumbers = function(resp, reloadOnComplete, onComplete) {
         this.vlAgent.handleResponse(resp,
diff --git a/Open-ILS/web/js/ui/default/acq/invoice/view.js b/Open-ILS/web/js/ui/default/acq/invoice/view.js
index 42f8377..f447f18 100644
--- a/Open-ILS/web/js/ui/default/acq/invoice/view.js
+++ b/Open-ILS/web/js/ui/default/acq/invoice/view.js
@@ -589,8 +589,19 @@ function addInvoiceItem(item) {
                     readOnly : invoice && openils.Util.isTrue(invoice.complete()),
                     dijitArgs : args,
                     parentNode : nodeByName(field, row)
-                })
-            )
+                }),
+                function(w) {
+                    if (field == "cost_billed") {
+                        dojo.connect(
+                            w, "onChange", function(value) {
+                                var paid = widgetRegistry.acqii[item.id()].amount_paid.widget;
+                                if (value && isNaN(paid.attr("value")))
+                                    paid.attr("value", value);
+                            }
+                        );
+                    }
+                }
+            );
         }
     );
 
@@ -819,7 +830,7 @@ function addInvoiceEntry(entry) {
                     } else {
                         dijitArgs.style = 'width:9em;';
                     }
-                    if(entry.isnew() && field == 'phys_item_count') {
+                    if (entry.isnew() && (field == 'phys_item_count' || field == 'inv_item_count')) {
                         // by default, attempt to pay for all non-canceled and as-of-yet-un-invoiced items
                         var count = Number(li.order_summary().item_count() || 0) - 
                                     Number(li.order_summary().cancel_count() || 0) -
@@ -854,6 +865,16 @@ function addInvoiceEntry(entry) {
                                 )
                             } // if
 
+                            if (field == "cost_billed") {
+                                // hooks applied with dojo.connect to dijit events are additive, so there's no conflict between this and what comes next
+                                dojo.connect(
+                                    w, "onChange", function(value) {
+                                    var paid = widgetRegistry.acqie[entry.id()].amount_paid.widget;
+                                        if (value && isNaN(paid.attr("value")))
+                                            paid.attr("value", value);
+                                    }
+                                );
+                            }
                             if(field == 'inv_item_count' || field == 'cost_billed') {
                                 setPerCopyPrice(row, entry);
                                 // update the per-copy count as invoice count and cost billed change 
diff --git a/Open-ILS/web/js/ui/default/acq/lineitem/related.js b/Open-ILS/web/js/ui/default/acq/lineitem/related.js
index 0f3b91b..9ae3c5f 100644
--- a/Open-ILS/web/js/ui/default/acq/lineitem/related.js
+++ b/Open-ILS/web/js/ui/default/acq/lineitem/related.js
@@ -4,6 +4,7 @@ dojo.require("openils.XUL");
 dojo.require("openils.CGI");
 dojo.require("openils.PermaCrud");
 dojo.require('openils.BibTemplate');
+dojo.require('openils.widget.PCrudAutocompleteBox');
 dojo.require('fieldmapper.OrgUtils');
 
 dojo.requireLocalization('openils.acq', 'acq');
@@ -142,49 +143,19 @@ function load() {
     }
 
     liTable = new AcqLiTable();
+    liTable.enableActionsDropdownOptions("vp");
     liTable.reset();
     liTable._isRelatedViewer = true;
 
     prepareButtons();
     fetchRelated();
-    dojo.connect(addToPoSave, 'onClick', addToPo)
-    openils.Util.registerEnterHandler(addToPoInput.domNode, addToPo);
-}
-
-var _addToPoHappened = false;
-function addToPo(args) {
-    var poId = addToPoInput.attr('value');
-    if (!poId) return false;
-    if (_addToPoHappened) return false;
-
-    var liId =  liTable.getSelected()[0].id();
-    console.log("adding li " + liId + " to PO " + poId);
-
-    // hmm, addToPo is invoked twice for some reason...
-    _addToPoHappened = true;
 
-    fieldmapper.standardRequest(
-        ['open-ils.acq', 'open-ils.acq.purchase_order.add_lineitem'],
-        {   async : true,
-            params : [openils.User.authtoken, poId, liId],
-            oncomplete : function(r) {
-                var resp = openils.Util.readResponse(r);
-                if (resp.success) {
-                    location.href = oilsBasePath + '/acq/po/view/' + poId;
-                } else {
-                    _addToPoHappened = false;
-                    if (resp.error == 'bad-po-state') {
-                        alert(localeStrings.ADD_LI_TO_PO_BAD_PO_STATE);
-                    } else if (resp.error == 'bad-li-state') {
-                        alert(localeStrings.ADD_LI_TO_PO_BAD_LI_STATE);
-                    }
-                }
-            }
-        }
-    );
-
-    addToPoDialog.hide();
-    return false; // prevent form submission
+    /* addToPoDialog now requires this function be defined to tell it what
+     * lineitem IDs to add to the PO.  Part of making it reusable in more
+     * places. */
+    addToPoDialog._get_li = function() {
+        return liTable.getSelected()[0].id();
+    };
 }
 
 openils.Util.addOnLoad(load);
diff --git a/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js b/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js
index 4345728..f0fa454 100644
--- a/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js
+++ b/Open-ILS/web/js/ui/default/acq/picklist/bib_search.js
@@ -25,6 +25,7 @@ var liTable;
 
 function drawForm() {
     liTable = new AcqLiTable();
+    liTable.enableActionsDropdownOptions("fs");
     liTable.skipInitialEligibilityCheck = true;
 
     fieldmapper.standardRequest(
diff --git a/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js b/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js
index 4853d16..a737c0b 100644
--- a/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js
+++ b/Open-ILS/web/js/ui/default/acq/picklist/from_bib.js
@@ -67,7 +67,10 @@ function init() {
     new openils.widget.XULTermLoader(
         {"parentNode": "acq-frombib-upload", "parseCSV": true}
     ).build(function(w) { termLoader = w; });
+
     liTable = new AcqLiTable();
+    liTable.enableActionsDropdownOptions("vp");
+
     pager = new LiTablePager(fetchRecords, liTable);
 
     openils.Util.show("acq-frombib-begin-holder");
diff --git a/Open-ILS/web/js/ui/default/acq/picklist/view.js b/Open-ILS/web/js/ui/default/acq/picklist/view.js
index 6fa02df..05bf842 100644
--- a/Open-ILS/web/js/ui/default/acq/picklist/view.js
+++ b/Open-ILS/web/js/ui/default/acq/picklist/view.js
@@ -14,6 +14,8 @@ var liTable;
 function load() {
     liTable = new AcqLiTable();
     liTable.isPL = plId;
+    liTable.enableActionsDropdownOptions("pl");
+
     fieldmapper.standardRequest(
         ['open-ils.acq', 'open-ils.acq.picklist.retrieve.authoritative'],
         {   async: true,
diff --git a/Open-ILS/web/js/ui/default/acq/po/search.js b/Open-ILS/web/js/ui/default/acq/po/search.js
index 5ef4d24..ddf7f3a 100644
--- a/Open-ILS/web/js/ui/default/acq/po/search.js
+++ b/Open-ILS/web/js/ui/default/acq/po/search.js
@@ -97,6 +97,7 @@ function loadMetaPO(fields) {
     } else {
         if (!metaPO) {
             metaPO = new AcqLiTable();
+            metaPo.enableActionsDropdownOptions("po");
 
             /* 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. */
diff --git a/Open-ILS/web/js/ui/default/acq/po/view_po.js b/Open-ILS/web/js/ui/default/acq/po/view_po.js
index 2b9cc4b..2a9b44c 100644
--- a/Open-ILS/web/js/ui/default/acq/po/view_po.js
+++ b/Open-ILS/web/js/ui/default/acq/po/view_po.js
@@ -299,6 +299,9 @@ function prepareInvoiceFeatures() {
     openils.Util.show("acq-po-invoice-stuff", "table-cell");
 }
 
+/* renderPo() is the best place to add tests that depend on PO-state
+ * (or simple ordered-or-not? checks) to enable/disable UI elements
+ * across the whole interface. */
 function renderPo() {
     var po_state = PO.state();
     dojo.byId("acq-po-view-id").innerHTML = PO.id();
@@ -314,27 +317,45 @@ function renderPo() {
 
     if(PO.order_date()) {
         openils.Util.show('acq-po-activated-on', 'inline');
+        liTable.enableActionsDropdownOptions("ao"); /* activated */
+
         dojo.byId('acq-po-activated-on').innerHTML = 
             dojo.string.substitute(
                 localeStrings.PO_ACTIVATED_ON, [
                     openils.Util.timeStamp(PO.order_date(), {formatLength:'short'})
                 ]
             );
-        if(po_state == "on-order" || po_state == "cancelled") {
-            dojo.removeAttr('receive_po', 'disabled');
-        } else if(po_state == "received") {
-            dojo.removeAttr('rollback_receive_po', 'disabled');
-        }
+        /* These are handled another way now */
+//        if (po_state == "on-order" || po_state == "cancelled") {
+//            dojo.removeAttr('receive_lineitems', 'disabled');
+//        } else if(po_state == "received") {
+//            dojo.removeAttr('rollback_receive_lineitems', 'disabled');
+//        }
+
+        /* cancel widgets only make sense for activate (ordered) POs */
+        makeCancelWidget(
+            dojo.byId("acq-po-view-cancel-reason"),
+            dojo.byId("acq-po-cancel-label")
+        );
+
+        /* likewise for invoice features */
+        openils.Util.show("acq-po-invoice-label", "table-cell");
+        prepareInvoiceFeatures();
+    } else {
+        /* These things only make sense for not-ordered-yet POs */
+
+        liTable.enableActionsDropdownOptions("po");
+
+        openils.Util.show("acq-po-zero-activate-label", "table-cell");
+        openils.Util.show("acq-po-zero-activate", "table-cell");
+
+        openils.Util.show("acq-po-item-table-controls");
     }
 
     makePrepayWidget(
         dojo.byId("acq-po-view-prepay"),
         openils.Util.isTrue(PO.prepayment_required())
     );
-    makeCancelWidget(
-        dojo.byId("acq-po-view-cancel-reason"),
-        dojo.byId("acq-po-cancel-label")
-    );
     // dojo.byId("acq-po-view-notes").innerHTML = PO.notes().length;
     poNoteTable.updatePoNotesCount();
 
@@ -380,8 +401,6 @@ function renderPo() {
             }
         );
     }
-
-    prepareInvoiceFeatures();
 }
 
 
@@ -472,7 +491,7 @@ function init2() {
 
 function checkCouldActivatePo() {
     var d = dojo.byId("acq-po-activate-checking");
-    var a = dojo.byId("acq-po-activate-link");
+    var a = dojo.byId("acq-po-activate-link");  /* <span> not <a> now, but no diff */
     d.innerHTML = localeStrings.PO_CHECKING;
     var warnings = [];
     var stops = [];
@@ -509,6 +528,7 @@ function checkCouldActivatePo() {
                 if (!(warnings.length || stops.length || other.length)) {
                     d.innerHTML = localeStrings.PO_COULD_ACTIVATE;
                     openils.Util.show(a, "inline");
+                    activatePoButton.attr("disabled", false);
                 } else {
                     if (other.length) {
                         /* XXX make the textcode part a tooltip one day */
@@ -549,6 +569,7 @@ function checkCouldActivatePo() {
                                 ]
                             );
                         openils.Util.show(a, "inline");
+                        activatePoButton.attr("disabled", false);
                     }
                 }
             }
@@ -557,14 +578,20 @@ function checkCouldActivatePo() {
 }
 
 function activatePo() {
+    activatePoButton.attr("disabled", true);
+
     if (openils.Util.isTrue(PO.prepayment_required())) {
-        if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER))
+        if (!confirm(localeStrings.PREPAYMENT_REQUIRED_REMINDER)) {
+            activatePoButton.attr("disabled", false);
             return false;
+        }
     }
 
     if (PO._warning_hack) {
-        if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM))
+        if (!confirm(localeStrings.PO_FUND_WARNING_CONFIRM)) {
+            activatePoButton.attr("disabled", false);
             return false;
+        }
     }
 
     liTable.showAssetCreator(activatePoStage2);
@@ -584,10 +611,11 @@ function activatePoStage2() {
                 {zero_copy_activate : dojo.byId('acq-po-activate-zero-copies').checked}
             ],
             "onresponse": function(r) {
+                progressDialog.hide();
+                activatePoButton.attr("disabled", false);
                 want_refresh = Boolean(openils.Util.readResponse(r));
             },
             "oncomplete": function() {
-                progressDialog.hide();
                 if (want_refresh)
                     location.href = location.href;
             }
diff --git a/Open-ILS/web/js/ui/default/acq/search/unified.js b/Open-ILS/web/js/ui/default/acq/search/unified.js
index a6be3f0..a59f4eb 100644
--- a/Open-ILS/web/js/ui/default/acq/search/unified.js
+++ b/Open-ILS/web/js/ui/default/acq/search/unified.js
@@ -976,8 +976,11 @@ openils.Util.addOnLoad(
 
         termManager = new TermManager();
 
+        var li_table = new AcqLiTable();
+        li_table.enableActionsDropdownOptions("gs");
+
         resultManager = new ResultManager(
-            new LiTablePager(null, new AcqLiTable()),
+            new LiTablePager(null, li_table),
             dijit.byId("acq-unified-po-grid"),
             dijit.byId("acq-unified-pl-grid"),
             dijit.byId("acq-unified-inv-grid")
diff --git a/docs/RELEASE_NOTES_NEXT/acq_po_interface_improvements.txt b/docs/RELEASE_NOTES_NEXT/acq_po_interface_improvements.txt
new file mode 100644
index 0000000..1570948
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/acq_po_interface_improvements.txt
@@ -0,0 +1,23 @@
+Acquisitions Purchase Order Improvements
+========================================
+
+Feature Summary
+---------------
+
+The following features, which primarily affect the user interface layer,
+improve Acquisitions work flows.
+
+  * Avoid double-activation of POs
+  * Disable invoice and cancel options for pending POs
+  * Disable zero-copy checkbox for activated POs
+  * Disable new charges for activated POs
+  * Replace "Shelving Location" with Copy Location
+
+  * Rearranging "actions" drop-down
+    ** More consitency in actions applying to selected lineitems specifically
+    ** Things moved from the per-lineitem dropdown to the main one when
+       sensible.
+    ** Add to PO dialog added
+    ** You can no longer add lineitems to a PO if they're already on one.
+    ** Actions in dropdown now enabled/disabled differently depending on
+       the interface where it appears (PO vs Selection List vs Acq Search etc.)

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |    2 +-
 .../perlmods/lib/OpenILS/Application/Acq/Order.pm  |  171 ++++++++++++++--
 Open-ILS/src/templates/acq/common/add_to_po.tt2    |   57 ++++++
 Open-ILS/src/templates/acq/common/li_table.tt2     |   70 ++++---
 Open-ILS/src/templates/acq/invoice/receive.tt2     |    2 +-
 Open-ILS/src/templates/acq/invoice/view.tt2        |    8 +-
 Open-ILS/src/templates/acq/lineitem/related.tt2    |   15 +--
 Open-ILS/src/templates/acq/po/item_table.tt2       |    2 +-
 Open-ILS/src/templates/acq/po/view.tt2             |    8 +-
 .../conify/global/acq/distribution_formula.tt2     |    2 +-
 .../src/templates/conify/global/acq/provider.tt2   |    2 +-
 Open-ILS/web/css/skin/default/acq.css              |    2 +
 Open-ILS/web/js/dojo/openils/acq/nls/acq.js        |    5 +-
 .../web/js/ui/default/acq/common/inv_dialog.js     |   13 ++-
 Open-ILS/web/js/ui/default/acq/common/li_table.js  |  209 +++++++++++---------
 Open-ILS/web/js/ui/default/acq/invoice/view.js     |   27 +++-
 Open-ILS/web/js/ui/default/acq/lineitem/related.js |   45 +----
 .../web/js/ui/default/acq/picklist/bib_search.js   |    1 +
 .../web/js/ui/default/acq/picklist/from_bib.js     |    3 +
 Open-ILS/web/js/ui/default/acq/picklist/view.js    |    2 +
 Open-ILS/web/js/ui/default/acq/po/search.js        |    1 +
 Open-ILS/web/js/ui/default/acq/po/view_po.js       |   58 ++++--
 Open-ILS/web/js/ui/default/acq/search/unified.js   |    5 +-
 .../acq_po_interface_improvements.txt              |   23 +++
 24 files changed, 507 insertions(+), 226 deletions(-)
 create mode 100644 Open-ILS/src/templates/acq/common/add_to_po.tt2
 create mode 100644 docs/RELEASE_NOTES_NEXT/acq_po_interface_improvements.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list