Acq: several interface improvements
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 26 Feb 2013 19:28:40 +0000 (14:28 -0500)
committerBill Erickson <berick@esilibrary.com>
Wed, 13 Mar 2013 19:51:26 +0000 (15:51 -0400)
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@esilibrary.com>
Signed-off-by: Bill Erickson <berick@esilibrary.com>
24 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
Open-ILS/src/templates/acq/common/add_to_po.tt2 [new file with mode: 0644]
Open-ILS/src/templates/acq/common/li_table.tt2
Open-ILS/src/templates/acq/invoice/receive.tt2
Open-ILS/src/templates/acq/invoice/view.tt2
Open-ILS/src/templates/acq/lineitem/related.tt2
Open-ILS/src/templates/acq/po/item_table.tt2
Open-ILS/src/templates/acq/po/view.tt2
Open-ILS/src/templates/conify/global/acq/distribution_formula.tt2
Open-ILS/src/templates/conify/global/acq/provider.tt2
Open-ILS/web/css/skin/default/acq.css
Open-ILS/web/js/dojo/openils/acq/nls/acq.js
Open-ILS/web/js/ui/default/acq/common/inv_dialog.js
Open-ILS/web/js/ui/default/acq/common/li_table.js
Open-ILS/web/js/ui/default/acq/invoice/view.js
Open-ILS/web/js/ui/default/acq/lineitem/related.js
Open-ILS/web/js/ui/default/acq/picklist/bib_search.js
Open-ILS/web/js/ui/default/acq/picklist/from_bib.js
Open-ILS/web/js/ui/default/acq/picklist/view.js
Open-ILS/web/js/ui/default/acq/po/search.js
Open-ILS/web/js/ui/default/acq/po/view_po.js
Open-ILS/web/js/ui/default/acq/search/unified.js
docs/RELEASE_NOTES_NEXT/acq_po_interface_improvements.txt [new file with mode: 0644]

index 75f0e32..1380f85 100644 (file)
@@ -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" />
index a86d4e6..ca49a79 100644 (file)
@@ -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 (file)
index 0000000..370bd28
--- /dev/null
@@ -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>
index 8e1c209..064ff52 100644 (file)
@@ -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>
                                 <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" />
                                         <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>
                     <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>
         </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 -->
             <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>
             <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>
             <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>
         <div jsId="acqLitLinkInvoiceDialog" dojoType="dijit.Dialog">
             [% INCLUDE "acq/common/inv_dialog.tt2" which = "li" %]
         </div>
+        [% INCLUDE "acq/common/add_to_po.tt2" %]
     </div>
 
 
                     <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>
index 1a964a6..80756cf 100644 (file)
@@ -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>
index 3871c64..678eec5 100644 (file)
@@ -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>
                         </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>
index fe2f527..337a64f 100644 (file)
     </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 %]
index 80497c5..be61c8e 100644 (file)
@@ -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>
index 2bc08e6..143a9d2 100644 (file)
@@ -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">
                             <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'>
index e3dfd1a..d80e29e 100644 (file)
@@ -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>
index 7040320..1eeafab 100644 (file)
                     <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>
index e881e26..cfa600d 100644 (file)
@@ -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; }
index fd7949f..ad82f7b 100644 (file)
@@ -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"
 }
index 7a5789e..6ef5126 100644 (file)
@@ -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;
index d1cfe99..b58f612 100644 (file)
@@ -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,
index 42f8377..f447f18 100644 (file)
@@ -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 
index 0f3b91b..9ae3c5f 100644 (file)
@@ -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);
index 4345728..f0fa454 100644 (file)
@@ -25,6 +25,7 @@ var liTable;
 
 function drawForm() {
     liTable = new AcqLiTable();
+    liTable.enableActionsDropdownOptions("fs");
     liTable.skipInitialEligibilityCheck = true;
 
     fieldmapper.standardRequest(
index 4853d16..a737c0b 100644 (file)
@@ -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");
index 6fa02df..05bf842 100644 (file)
@@ -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,
index 5ef4d24..ddf7f3a 100644 (file)
@@ -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. */
index 2b9cc4b..2a9b44c 100644 (file)
@@ -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;
             }
index a6be3f0..a59f4eb 100644 (file)
@@ -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 (file)
index 0000000..1570948
--- /dev/null
@@ -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.)