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

Evergreen Git git at git.evergreen-ils.org
Wed Aug 1 14:24:55 EDT 2012


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  460d34e561fbeb9e9c878fb2f7faa45b260ee9a3 (commit)
       via  8b27613b0bf67e9c14d1d8ed3121d544c8bc41c5 (commit)
       via  d7177aa1a4ad46d00dbea4489e3388d0ba1cc772 (commit)
       via  f502f9e4265603e47f9747d4dc30ee73c26dac07 (commit)
       via  07ba14c420cfda48dc550cf0888fd96d29603e76 (commit)
       via  7b12ae81a56b1d998eb6c9f239ef259d3e23027a (commit)
      from  8ab88c90f5c4e71c0d5adea27b54bdb472c6bd0a (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 460d34e561fbeb9e9c878fb2f7faa45b260ee9a3
Author: Bill Erickson <berick at esilibrary.com>
Date:   Wed Aug 1 13:54:00 2012 -0400

    ACQ unified search submit on enter (for real)
    
    This replaces the old-style submit on enter handler which was based
    attaching key handlers to the dijit domNode, which does not work for
    filtering selects (i.e. more complicated dijits) w/ the dijit-sanctioned
    dojo.connect(..., 'onkeyup', ...), which works regardless.  This also
    covers more form input elements which were overlooked with the original
    submit-on-enter code.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

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 2c1eff0..6f6142f 100644
--- a/Open-ILS/web/js/ui/default/acq/search/unified.js
+++ b/Open-ILS/web/js/ui/default/acq/search/unified.js
@@ -165,6 +165,16 @@ function TermSelectorFactory(terms) {
                     wStore[widgetKey].focus();
                 if (typeof(callback) == "function")
                     callback(term, widgetKey);
+
+                // submit on enter
+                dojo.connect(wStore[widgetKey], 'onkeyup',
+                    function(e) {
+                        if(e.keyCode == dojo.keys.ENTER) {
+                            resultManager.go(termManager.buildSearchObject());
+                        }
+                    }
+                );
+
             } else {
                 new openils.widget.AutoFieldWidget({
                     "fmClass": term.hint,
@@ -186,9 +196,11 @@ function TermSelectorFactory(terms) {
                             callback(term, widgetKey);
 
                         // submit on enter
-                        openils.Util.registerEnterHandler(w.domNode,
-                            function() { 
-                                resultManager.go(termManager.buildSearchObject());
+                        dojo.connect(w.domNode, 'onkeyup',
+                            function(e) {
+                                if(e.keyCode == dojo.keys.ENTER) {
+                                    resultManager.go(termManager.buildSearchObject());
+                                }
                             }
                         );
                     }

commit 8b27613b0bf67e9c14d1d8ed3121d544c8bc41c5
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Jul 12 15:26:50 2012 -0400

    ACQ invoice formatting improvements
    
    Prevent # invoiced and # paid inputs from wrapping.  Also, make the text
    boxes a little smaller to reduce horizontal space requirements.  It's
    still possible to enter any value, but only the 4 least significant
    digits are visible.  Since it's not common to invoice 10k+ copies of the
    same item, this seems like a fair trade off.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/templates/acq/invoice/view.tt2 b/Open-ILS/src/templates/acq/invoice/view.tt2
index 6ed29f1..c1a7aa7 100644
--- a/Open-ILS/src/templates/acq/invoice/view.tt2
+++ b/Open-ILS/src/templates/acq/invoice/view.tt2
@@ -46,7 +46,6 @@
 
     <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
 
-
         <div dojoType="dijit.layout.TabContainer" style="width: 96%; height: 100%;">
             <div dojoType="dijit.layout.ContentPane" 
                 class='oils-acq-detail-content-pane' title="Invoice" selected='true' style='height:600px'>
@@ -84,7 +83,7 @@
                                 <div name='title_details'></div>
                                 <div name='note'></div>
                             </td>
-                            <td class='acq-invoice-center-col'>
+                            <td class='acq-invoice-center-col' nowrap='nowrap'>
                                 <span name='inv_item_count'></span>&nbsp;/&nbsp;<span name='phys_item_count'></span>
                             </td>
                             <td class='acq-invoice-billed-col'><div name='cost_billed'></div></td>
@@ -301,7 +300,6 @@
             </div> <!-- tab 2 -->
         </div> <!-- end tabcontainer -->
     </div> <!-- end contentpane -->
-
 </div>
 <div dojoType='openils.widget.ProgressDialog' jsId='progressDialog'></div>
 <div jsId='extraItemsDialog' dojoType="dijit.Dialog" title="Extra Items">
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 4f1fa60..bd1ff0a 100644
--- a/Open-ILS/web/js/ui/default/acq/invoice/view.js
+++ b/Open-ILS/web/js/ui/default/acq/invoice/view.js
@@ -747,7 +747,11 @@ function addInvoiceEntry(entry) {
                 ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
                 function(field) {
                     var dijitArgs = {required : true, constraints : {min: 0}, style : 'width:6em'};
-                    if(!field.match(/count/)) dijitArgs.style = 'width:9em';
+                    if(field.match(/count/)) {
+                        dijitArgs.style = 'width:4em;';
+                    } else {
+                        dijitArgs.style = 'width:9em;';
+                    }
                     if(entry.isnew() && field == 'phys_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) - 

commit d7177aa1a4ad46d00dbea4489e3388d0ba1cc772
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Jul 10 11:07:53 2012 -0400

    ACQ invoice inline lineitem search and add
    
    The Invoice UI is how composed of two tabs, the main invoice tab and a new
    Search tab.  The search tab consists of a subset of the Acquisitions unified
    search interface.  The goal is to allow users to search for lineitems to
    invoice.  Search results may be added directly to the growing invoice.  A
    number of small usability features are included.
    
    Features
    ~~~~~~~~
    
        * Option (default) to limit searches to invoiceable items.
            ** These are lineitems that are not cancelled, have at least one
               invoiceable copy, linked to a PO whose provider matches that of the
               current invoice, and are not already linked to the current invoice.
    
        * Search defaults to last-run search (on workstation).
        * New Lineitem Detail filter options
        * Sort searches by lineitem number (default) and title.
        * There is a new Expected Cost field which includes both the total invoiced
          cost plus the anticipated cost of lineitems as they are added.
        * New Price per Copy field
        * Lineitem count field
        * Show / Hide Invoice details button.  Details are displayed by default, but
          hidden when the user enters the search tab.  From there it remains hidden
          until manually shown (or a new invoice is opened).
        * A new "Save & Clear" button which saves the current invoice then clears
          the invoice display to create a new invoice.
        * Provider, shipper, and receiver fields are auto-populated from the
          first-added invoice data (when not already set).
        * Totals are now read-only, since they are derived from existing data (and
          are informational only).
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Search.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Search.pm
index cbf4c73..da594a2 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Search.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Search.pm
@@ -229,7 +229,7 @@ sub prepare_terms {
     my $conj = $is_and ? "-and" : "-or";
     my $outer_clause = {};
 
-    foreach my $class (qw/acqpo acqpl acqinv jub/) {
+    foreach my $class (qw/acqpo acqpl acqinv jub acqlid acqlisum acqlisumi/) {
         next if not exists $terms->{$class};
 
         $outer_clause->{$conj} = [] unless $outer_clause->{$conj};
@@ -243,7 +243,12 @@ sub prepare_terms {
             } elsif ($between and could_be_range($v)) {
                 $term_clause = {$k => {"between" => $v}};
             } elsif (check_1d_max($v)) {
-                $v = castdate($v, $gte, $lte) if $castdate;
+                if ($castdate) {
+                    $v = castdate($v, $gte, $lte) if $castdate;
+                } elsif ($gte or $lte) {
+                    my $op = $gte ? '>=' : '<=';
+                    $v = {$op => $v};
+                }
                 $term_clause = {$k => $v};
             } else {
                 next;
@@ -339,6 +344,11 @@ sub build_from_clause_and_joins {
         } else {
             $graft_map{$class} = $query->{from}{$core}{$class} ||= {};
             $graft_map{$class}{type} = $join_type;
+
+            # without this, the SQL attempts to join on 
+            # jub.order_summary, which is a virtual field.
+            $graft_map{$class}{field} = 'lineitem' 
+                if $class eq 'acqlisum' or $class eq 'acqlisumi';
         }
     }
 
@@ -415,6 +425,7 @@ sub unified_search {
         $hint => [{"column" => "id", "transform" => "distinct"}]
     };
 
+    my $attr_from_filter;
     if ($options->{"order_by"}) {
         # What's the point of this block?  When using ORDER BY in conjuction
         # with SELECT DISTINCT, the fields present in ORDER BY have to also
@@ -432,9 +443,30 @@ sub unified_search {
 q/order_by clause must be of the long form, like:
 "order_by": [{"class": "foo", "field": "bar", "direction": "asc"}]/
             );
+
         } else {
+
+            # we can't combine distinct(id) with another select column, 
+            # since the non-distinct column may arbitrarily (via hash keys)
+            # sort to the front of the final SQL, which PG will complain about.  
+            $select_clause = { $hint => ["id"] };
             $select_clause->{$class} ||= [];
-            push @{$select_clause->{$class}}, $field;
+            push @{$select_clause->{$class}}, 
+                {column => $field, transform => 'first', aggregate => 1};
+
+            # when sorting by LI attr values, we have to limit 
+            # to a specific type of attr value to sort on.
+            if ($class eq 'acqlia') {
+                $attr_from_filter = {
+                    "fkey" => "id",
+                    "filter" => {
+                        "attr_type" => "lineitem_marc_attr_definition",
+                        "attr_name" => $options->{"order_by_attr"} || "title"
+                    },
+                    "type" => "left",
+                    "field" =>"lineitem"
+                };
+            }
         }
     }
 
@@ -469,6 +501,14 @@ q/order_by clause must be of the long form, like:
         return new OpenILS::Event("BAD_PARAMS", "desc" => "No usable terms");
     }
 
+
+    # if ordering by acqlia, insert the from clause 
+    # filter to limit to one type of attr.
+    if ($attr_from_filter) {
+        $query->{from}->{jub} = {} unless $query->{from}->{jub};
+        $query->{from}->{jub}->{acqlia} = $attr_from_filter;
+    }
+
     my $results = $e->json_query($query) or return $e->die_event;
     my @id_list = map { $_->{"id"} } (grep { $_->{"id"} } @$results);
 
diff --git a/Open-ILS/src/templates/acq/invoice/view.tt2 b/Open-ILS/src/templates/acq/invoice/view.tt2
index 7e4d489..6ed29f1 100644
--- a/Open-ILS/src/templates/acq/invoice/view.tt2
+++ b/Open-ILS/src/templates/acq/invoice/view.tt2
@@ -1,122 +1,307 @@
 [% WRAPPER 'base.tt2' %]
 [% ctx.page_title = 'Invoicing' %]
-<script type="text/javascript">var invoiceId = '[% ctx.page_args.0 %]';</script>
 <div dojoType="dijit.layout.ContentPane" style="height:100%">
 
     <div dojoType="dijit.layout.ContentPane" layoutAlign="client" class='oils-header-panel'>
         <div> Invoice </div>
-        <div id="acq-view-invoice-receive" class="hidden"><button id="acq-view-invoice-receive-link">Receive Items</button></div>
+        <div id="acq-view-invoice-receive" class="hidden">
+            <button id="acq-view-invoice-receive-link">Receive Items</button>
+        </div>
     </div>
 
     <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
-        <div id='acq-view-invoice-div'></div>
-    </div>
+        <style>
+            #acq-invoice-num-summary-table td {
+                padding-right: 8px;
+                padding-top: 5px;
+                padding-bottom: 5px;
+                border-bottom: 2px solid #888;
+            }
+            #acq-invoice-num-summary-table td[id] {
+                font-weight:bold;
+            }
+        </style>
+        <table id='acq-invoice-num-summary-table'>
+            <tr><td>[% l("Lineitems: " ) %]</td><td id='acq-invoice-summary-count'>0</td></tr>
+            <tr>
+                <td>[% l("Expected Cost: " ) %]</td>
+                <td id='acq-invoice-summary-cost'>0.00</td>
+            </tr>
+        </table>
+        <br/>
+    <div>
 
     <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
-        <table class='oils-acq-invoice-table'>
-            <thead/>
-            <tbody id='acq-invoice-entry-header' class='hidden'>
-                <tr>
-                    <td colspan='0'>
-                        <h3>Bibliographic Items</h3>
-                    </td>
-                </tr>
-            </tbody>
-            <!-- acq.invoice_entry -->
-            <thead id='acq-invoice-entry-thead' class='hidden'>
-                <tr>
-                    <th colspan='2'>Title Details</th>
-                    <th class='acq-invoice-center-col'>#&nbsp;Invoiced&nbsp;/&nbsp;#&nbsp;Paid</th>
-                    <th class='acq-invoice-center-col'>Billed</th>
-                    <th class='acq-invoice-paid-col'>Paid</th>
-                    <th class='acq-invoice-center-col hide-complete'>Detach</th>
-                </tr>
-            </thead>
-            <tbody id='acq-invoice-entry-tbody' class='hidden'>
-                <tr id='acq-invoice-entry-template' class='acq-invoice-row'>
-                    <td colspan='2'>
-                        <div name='title_details'></div>
-                        <div name='note'></div>
-                    </td>
-                    <td class='acq-invoice-center-col'>
-                        <span name='inv_item_count'></span>&nbsp;/&nbsp;<span name='phys_item_count'></span>
-                    </td>
-                    <td class='acq-invoice-billed-col'><div name='cost_billed'></div></td>
-                    <td class='acq-invoice-paid-col'><div name='amount_paid'></div></td>
-                    <td class='acq-invoice-center-col hide-complete'><a href='javascript:void(0);' name='detach'>Detach</a></td>
-                </tr>
-            </tbody>
-            <tbody>
-                <tr>
-                    <td  style='margin-top:15px;' colspan='0'>
-                        <h3>Direct Charges, Taxes, Fees, etc.</h3>
-                    </td>
-                </tr>
-            </tbody>
-            <!-- acq.invoice_item -->
-            <thead>
-                <tr>
-                    <th>Charge Type</th>
-                    <th class='acq-invoice-center-col'>Fund</th>
-                    <th>Title/Description</th>
-                    <th class='acq-invoice-center-col'>Billed</th>
-                    <th class='acq-invoice-paid-col'>Paid</th>
-                    <th class='acq-invoice-center-col hide-complete'>Delete</th>
-                </tr>
-            </thead>
-            <tbody id='acq-invoice-item-tbody'>
-                <tr id='acq-invoice-item-template' class='acq-invoice-row acq-invoice-item-row'>
-                    <td><div name='inv_item_type'></div></td>
-                    <td class='acq-invoice-center-col'><div name='fund'></div></td>
-                    <td><div name='title'></div></td>
-                    <td class='acq-invoice-center-col acq-invoice-billed-col'><div name='cost_billed'></div></td>
-                    <td class='acq-invoice-paid-col'><div name='amount_paid'></div></td>
-                    <td class='acq-invoice-center-col hide-complete'><a href='javascript:void(0);' name='delete'>Delete</a></td>
-                </tr>
-            </tbody>
-            <tbody class='hide-complete'>
-                <tr>
-                    <td colspan='0'>
-                        <a href='javascript:void(0);' id='acq-invoice-new-item'>Add Charge...</a>
-                    </td>
-                </tr>
-            </tbody>
-            <tbody>
-                <tr>
-                    <td style='margin-top:15px;' colspan='0'> 
-                        <h3> </h3>
-                    </td>
-                </tr>
-            </tbody>
-            <thead>
-                <tr>
-                    <th colspan='3'/>
-                    <th class='acq-invoice-center-col acq-invoice-billed-col'>Total</th>
-                    <th class='acq-invoice-paid-col'>Total</th>
-                    <th class='acq-invoice-center-col acq-invoice-balance-col'>Balance</th>
-                </tr>
-            </thead>
-            <tbody>
-                <tr>
-                    <td colspan='3' style='text-align:right;'>
-                        <button jsId='invoiceSaveButton' class='hide-complete'
-                            dojoType='dijit.form.Button' onclick='saveChanges();'>Save</button>
-                        <button jsId='invoiceProrateButton' class='hide-complete'
-                            dojoType='dijit.form.Button' onclick='saveChanges(true);'>Save &amp; Prorate</button>
-                        <button jsId='invoiceCloseButton' class='hide-complete'
-                            dojoType='dijit.form.Button' onclick='saveChanges(false, true);'>Save &amp; Close</button>
-                        <span class='hidden' id='acq-invoice-reopen-button-wrapper'>
-                            <button jsId='invoiceReopenButton' 
-                                dojoType='dijit.form.Button' onclick='saveChanges(false, false, true);'>Reopen Invoice</button>
-                        </span>
-                    </td>
-                    <td class='acq-invoice-center-col'><div jsId='totalInvoicedBox' dojoType='dijit.form.CurrencyTextBox' style='width:9em;'></div></td>
-                    <td class='acq-invoice-paid-col'><div jsId='totalPaidBox' dojoType='dijit.form.CurrencyTextBox' style='width:9em;'></div></td>
-                    <td class='acq-invoice-center-col'><div jsId='balanceOwedBox' dojoType='dijit.form.CurrencyTextBox' style='width:9em;'></div></td>
-                </tr>
-            </tbody>
-        </table>
+        <div id='acq-invoice-summary'>
+            <button id='acq-invoice-summary-toggle-off'>[% l('Hide Details') %]</button>
+            <div id='acq-view-invoice-div'></div>
+        </div>
+        <div id='acq-invoice-summary-small'>
+            <button id='acq-invoice-summary-toggle-on'>[% l('Show Details') %]</button>
+            <span style='font-weight:bold; font-size:120%' id='acq-invoice-summary-name'></span>
+            <br/>
+            <br/>
+        </div>
     </div>
+
+    <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+
+
+        <div dojoType="dijit.layout.TabContainer" style="width: 96%; height: 100%;">
+            <div dojoType="dijit.layout.ContentPane" 
+                class='oils-acq-detail-content-pane' title="Invoice" selected='true' style='height:600px'>
+
+                <script type='dojo/connect' event='onShow'>
+                    // the table is left at display=none on subsequent tab views
+                    dojo.byId('oils-acq-invoice-table').style.display = 'table'
+                </script>
+
+                <table id='oils-acq-invoice-table' class='oils-acq-invoice-table'>
+                    <thead/>
+                    <tbody id='acq-invoice-entry-header' class='hidden'>
+                        <tr>
+                            <td colspan='0'>
+                                <h3>
+                                    [% l('Bibliographic Items') %]
+                                </h3>
+                            </td>
+                        </tr>
+                    </tbody>
+                    <!-- acq.invoice_entry -->
+                    <thead id='acq-invoice-entry-thead' class='hidden'>
+                        <tr>
+                            <th colspan='2'>Title Details</th>
+                            <th class='acq-invoice-center-col'>#&nbsp;Invoiced&nbsp;/&nbsp;#&nbsp;Paid</th>
+                            <th class='acq-invoice-center-col'>Billed</th>
+                            <th class='acq-invoice-paid-per-copy-col'>Per Copy</th>
+                            <th class='acq-invoice-paid-col'>Paid</th>
+                            <th class='acq-invoice-center-col hide-complete'>Detach</th>
+                        </tr>
+                    </thead>
+                    <tbody id='acq-invoice-entry-tbody' class='hidden'>
+                        <tr id='acq-invoice-entry-template' class='acq-invoice-row'>
+                            <td colspan='2'>
+                                <div name='title_details'></div>
+                                <div name='note'></div>
+                            </td>
+                            <td class='acq-invoice-center-col'>
+                                <span name='inv_item_count'></span>&nbsp;/&nbsp;<span name='phys_item_count'></span>
+                            </td>
+                            <td class='acq-invoice-billed-col'><div name='cost_billed'></div></td>
+                            <td><div name='amount_paid_per_copy'>0.00</div></td>
+                            <td class='acq-invoice-paid-col'><div name='amount_paid'></div></td>
+                            <td class='acq-invoice-center-col hide-complete'><a href='javascript:void(0);' name='detach'>Detach</a></td>
+                        </tr>
+                    </tbody>
+                    <tbody>
+                        <tr>
+                            <td  style='margin-top:15px;' colspan='0'>
+                                <h3>Direct Charges, Taxes, Fees, etc.</h3>
+                            </td>
+                        </tr>
+                    </tbody>
+                    <!-- acq.invoice_item -->
+                    <thead>
+                        <tr>
+                            <th>Charge Type</th>
+                            <th class='acq-invoice-center-col'>Fund</th>
+                            <th>Title/Description</th>
+                            <th class='acq-invoice-center-col'>Billed</th>
+                            <th/>
+                            <th class='acq-invoice-paid-col'>Paid</th>
+                            <th class='acq-invoice-center-col hide-complete'>Delete</th>
+                        </tr>
+                    </thead>
+                    <tbody id='acq-invoice-item-tbody'>
+                        <tr id='acq-invoice-item-template' class='acq-invoice-row acq-invoice-item-row'>
+                            <td><div name='inv_item_type'></div></td>
+                            <td class='acq-invoice-center-col'><div name='fund'></div></td>
+                            <td><div name='title'></div></td>
+                            <td class='acq-invoice-center-col acq-invoice-billed-col'><div name='cost_billed'></div></td>
+                            <td/>
+                            <td class='acq-invoice-paid-col'><div name='amount_paid'></div></td>
+                            <td class='acq-invoice-center-col hide-complete'><a href='javascript:void(0);' name='delete'>Delete</a></td>
+                        </tr>
+                    </tbody>
+                    <tbody class='hide-complete'>
+                        <tr>
+                            <td colspan='0'>
+                                <a href='javascript:void(0);' id='acq-invoice-new-item'>Add Charge...</a>
+                            </td>
+                        </tr>
+                    </tbody>
+                    <tbody>
+                        <tr>
+                            <td style='margin-top:15px;' colspan='0'> 
+                                <h3> </h3>
+                            </td>
+                        </tr>
+                    </tbody>
+                    <thead>
+                        <tr>
+                            <th colspan='3'/>
+                            <th class='acq-invoice-center-col acq-invoice-billed-col'>Total</th>
+                            <th/>
+                            <th class='acq-invoice-paid-col'>Total</th>
+                            <th class='acq-invoice-center-col acq-invoice-balance-col'>Balance</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr>
+                            <td colspan='3' style='text-align:right;'>
+                                <button jsId='invoiceSaveButton' class='hide-complete'
+                                    dojoType='dijit.form.Button' onclick='saveChanges();'>[% l('Save') %]</button>
+                                <button jsId='invoiceSaveButton' class='hide-complete'
+                                    dojoType='dijit.form.Button' onclick='saveChanges({clear:true});'>[% l('Save &amp; Clear') %]</button>
+                                <button jsId='invoiceProrateButton' class='hide-complete'
+                                    dojoType='dijit.form.Button' onclick='saveChanges({prorate:true});'>[% l('Prorate') %]</button>
+                                <button jsId='invoiceCloseButton' class='hide-complete'
+                                    dojoType='dijit.form.Button' onclick='saveChanges({close:true});'>[% l('Close') %]</button>
+                                <span class='hidden' id='acq-invoice-reopen-button-wrapper'>
+                                    <button jsId='invoiceReopenButton' 
+                                        dojoType='dijit.form.Button' onclick='saveChanges({reopen:true});'>[% l('Reopen') %]</button>
+                                </span>
+                            </td>
+                            <td class='acq-invoice-center-col'><div id='acq-total-invoiced-box'></div></td>
+                            <td/>
+                            <td class='acq-invoice-paid-col'><div id='acq-total-paid-box'></div></td>
+                            <td class='acq-invoice-center-col'><div id='acq-total-balance-box'></div></td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div> <!-- tab 1 -->
+
+            <div dojoType="dijit.layout.ContentPane" 
+                class='oils-acq-detail-content-pane' title="Search">
+    
+                <script type='dojo/connect' event='onShow'>
+                    // hide summary info when opening the search tab
+                    dojo.byId('acq-invoice-summary-toggle-off').onclick();
+                    renderUnifiedSearch();
+                </script>
+
+
+                <!-- slim, inline unified search UI -->
+                <div id='oils-acq-invoice-search' _class='hidden'>
+
+                    <div id="acq-unified-form">
+                        <div>
+                            <label for="acq-unified-result-type">[% l('Search for') %]</label>
+                            <select id="acq-unified-result-type" disabled='disabled'>
+                                <option value="lineitem">line items</option>
+                            </select>
+                            <label for="acq-unified-conjunction">matching</label>
+                            <select id="acq-unified-conjunction">
+                                <option value="and">all</option>
+                                <option value="or">any</option>
+                            </select>
+                            <label for="acq-unified-conjunction">
+                                of the following terms:
+                            </label>
+                        </div>
+                        <div id="acq-unified-terms">
+                            <table id="acq-unified-terms-table">
+                                <tbody id="acq-unified-terms-tbody">
+                                    <tr id="acq-unified-terms-row-tmpl"
+                                        class="acq-unified-terms-row">
+                                        <td name="selector"
+                                            class="acq-unified-terms-selector"></td>
+                                        <td name="match"
+                                            class="acq-unified-terms-match">
+                                            <select>
+                                                <option value="">is</option>
+                                                <option value="__not">is NOT</option>
+                                                <option value="__fuzzy" disabled="disabled">
+                                                    contains
+                                                </option>
+                                                <option value="__not,__fuzzy"
+                                                    disabled="disabled">
+                                                    does NOT contain
+                                                </option>
+                                                <option value="__lte" disabled="disabled">
+                                                    is on or BEFORE
+                                                </option>
+                                                <option value="__gte" disabled="disabled">
+                                                    is on or AFTER
+                                                </option>
+                                                <option value="__in" disabled="disabled">
+                                                    matches a term from a file
+                                                </option>
+                                            </select>
+                                        </td>
+                                        <td name="widget"
+                                            class="acq-unified-terms-widget"></td>
+                                        <td name="remove"
+                                            class="acq-unified-terms-remove"></td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                        <div id="acq-unified-add-term">
+                            <button onclick="termManager.addRow()">Add Search Term</button>
+                        </div>
+                        <table width='100%'><tr>
+                            <td align='left'>
+                                <button onclick="performSearch(0)">[% l('Search') %]</button>
+                                <button onclick='addSelectedToInvoice()'>
+                                    [% l('Add Selected Items to Invoice') %]
+                                </button>
+                                <span id='acq-inv-search-prev'>
+                                    <a href='javascript:performSearch(-1)'>[% l('Previous') %]</a>
+                                </span>
+                                <span>
+                                    <a href='javascript:performSearch(1)'>[% l('Next') %]</a>
+                                </span>
+                            </td>
+                            <td align='right'>
+                                <input type='checkbox' id='acq-invoice-search-sort-title'/>
+                                [% l('Sort by title') %]
+                                <span style='padding-left:8px;'>
+                                <input type='checkbox' id='acq-invoice-search-limit-invoiceable' checked='checked'/>
+                                [% l('Limit to Invoiceable Items') %]
+                            </td>
+                        </tr></table>
+                    </div> <!-- end search form -->
+                    <div id='acq-unified-results-no_results'>
+                        <b>[% l('No Results') %]</b>
+                    </div>
+                    <div id='acq-unified-results-lineitem'>
+                        <style>
+                            #acq-invoice-search-results-tbody  { width: 100%; }
+                            #acq-invoice-search-results-tbody td { 
+                                padding: 5px; 
+                                border-bottom: 1px solid #888;
+                            }
+                            .search-resutls-select-td {
+                                padding-right: 8px; border-right: 2px solid #888;
+                            }
+                            .search-results-content-td {
+                                padding-left: 8px; border-left: 2px solid #888; 
+                            }
+                            .search-results-already-invoiced {
+                                background-color: #E99;
+                            }
+                        </style>
+                        <table>
+                            <tbody id='acq-invoice-search-results-tbody'>
+                                <tr id='acq-invoice-search-results-tr'>
+                                    <td class='search-resutls-select-td'>
+                                        <input type='checkbox' name='search-results-checkbox'/>
+                                    </td>
+                                    <td class='search-results-content-td'>
+                                        <div name='search-results-content-div'>
+                                            <img src='[% ctx.media_prefix %]/opac/images/progressbar_green.gif'/>
+                                        </div>
+                                    </td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div> <!-- tab 2 -->
+        </div> <!-- end tabcontainer -->
+    </div> <!-- end contentpane -->
+
 </div>
 <div dojoType='openils.widget.ProgressDialog' jsId='progressDialog'></div>
 <div jsId='extraItemsDialog' dojoType="dijit.Dialog" title="Extra Items">
@@ -132,6 +317,12 @@
         <button dojoType='dijit.form.Button' jsId='extraCopiesGo'>Add New Items</button>
     </div>
 </div>
+<script type="text/javascript">
+    var invoiceId = '[% ctx.page_args.0 %]';
+    window.unifiedSearchExternalMode = true;
+</script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/common/base64.js"></script>
+<script type="text/javascript" src="[% ctx.media_prefix %]/js/ui/default/acq/search/unified.js"></script>
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/invoice/common.js'> </script>
 <script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/acq/invoice/view.js'> </script>
 [% END %]
diff --git a/Open-ILS/web/css/skin/default/acq.css b/Open-ILS/web/css/skin/default/acq.css
index 6d03933..07fccb8 100644
--- a/Open-ILS/web/css/skin/default/acq.css
+++ b/Open-ILS/web/css/skin/default/acq.css
@@ -211,7 +211,7 @@ span[name="notes_alert_flag"] {color: #c00;font-weight: bold;font-size: 110%;mar
 .acq-inoice-item-extra-info { padding-left: 10px; }
 .acq-inoice-item-info { font-weight: bold; }
 .acq-invoice-row td { border-bottom: 1px solid #e0e0e0; }
-.acq-invoice-invalid-amount input { color: red; font-weight: bold; }
+.acq-invoice-invalid-amount  { color: red; font-weight: bold; }
 .acq-link-invoice-dialog td,.acq-link-invoice-dialog th {padding-top: 10px;}
 .acq-invoice-paid-col {background : #E0E0E0; text-align: center;}
 .acq-invoice-center-col { text-align: center; }
diff --git a/Open-ILS/web/js/ui/default/acq/invoice/common.js b/Open-ILS/web/js/ui/default/acq/invoice/common.js
index 03fade4..0980fca 100644
--- a/Open-ILS/web/js/ui/default/acq/invoice/common.js
+++ b/Open-ILS/web/js/ui/default/acq/invoice/common.js
@@ -4,18 +4,26 @@ dojo.require('openils.widget.EditPane');
 
 function drawInvoicePane(parentNode, inv, args) {
     args = args || {};
+    var pane;
 
     var override = {};
     if(!inv) {
         override = {
             recv_date : {widgetValue : dojo.date.stamp.toISOString(new Date())},
-            receiver : {widgetValue : openils.User.user.ws_ou()},
+            //receiver : {widgetValue : openils.User.user.ws_ou()},
             recv_method : {widgetValue : 'PPR'}
         };
     }
 
     dojo.mixin(override, {
-        provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
+        provider : { 
+            dijitArgs : { 
+                store_options : { base_filter : { active :"t" } },
+                onChange : function(val) {
+                    pane.setFieldValue('shipper', val);
+                }
+            } 
+        },
         shipper  : { dijitArgs : { store_options : { base_filter : { active :"t" } } } }
     });
 
@@ -23,7 +31,19 @@ function drawInvoicePane(parentNode, inv, args) {
         override[field] = {widgetValue : args[field]};
     }
 
-    var pane = new openils.widget.EditPane({
+    // push the name of the invoice into the name display field after update
+    override.inv_ident = dojo.mixin(
+        override.inv_ident,
+        {dijitArgs : {onChange :
+            function(newVal) {
+                if (dojo.byId('acq-invoice-summary-name'))
+                    dojo.byId('acq-invoice-summary-name').innerHTML = newVal;
+            }
+        }}
+    );
+
+
+    pane = new openils.widget.EditPane({
         fmObject : inv,
         paneStackCount : 2,
         fmClass : 'acqinv',
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 2e8f499..4f1fa60 100644
--- a/Open-ILS/web/js/ui/default/acq/invoice/view.js
+++ b/Open-ILS/web/js/ui/default/acq/invoice/view.js
@@ -1,6 +1,8 @@
 dojo.require('dojo.date.locale');
 dojo.require('dojo.date.stamp');
+dojo.require('dojo.cookie');
 dojo.require('dijit.form.CheckBox');
+dojo.require('dijit.form.Button');
 dojo.require('dijit.form.CurrencyTextBox');
 dojo.require('dijit.form.NumberTextBox');
 dojo.require('openils.User');
@@ -36,6 +38,9 @@ var extraCopies = {};
 var extraCopiesFund;
 var widgetRegistry = {acqie : {}, acqii : {}};
 var focusLineitem;
+var searchInitDone = false;
+var termManager;
+var resultManager;
 
 function nodeByName(name, context) {
     return dojo.query('[name='+name+']', context)[0];
@@ -53,12 +58,30 @@ function init() {
 
     focusLineitem = new openils.CGI().param('focus_li');
 
+    totalInvoicedBox = dojo.byId('acq-total-invoiced-box');
+    totalPaidBox = dojo.byId('acq-total-paid-box');
+    balanceOwedBox = dojo.byId('acq-total-balance-box');
+
     itemTypes = pcrud.retrieveAll('aiit');
 
+    dojo.byId('acq-invoice-summary-toggle-off').onclick = function() {
+        openils.Util.hide(dojo.byId('acq-invoice-summary'));
+        openils.Util.show(dojo.byId('acq-invoice-summary-small'));
+    };
+
+    dojo.byId('acq-invoice-summary-toggle-on').onclick = function() {
+        openils.Util.show(dojo.byId('acq-invoice-summary'));
+        openils.Util.hide(dojo.byId('acq-invoice-summary-small'));
+    }
+
     if(cgi.param('create')) {
         renderInvoice();
 
+        // show summary info by default for new invoices
+        dojo.byId('acq-invoice-summary-toggle-on').onclick();
+
     } else {
+        dojo.byId('acq-invoice-summary-toggle-off').onclick();
         fieldmapper.standardRequest(
             ['open-ils.acq', 'open-ils.acq.invoice.retrieve.authoritative'],
             {
@@ -117,16 +140,20 @@ function renderInvoice() {
         );
     }
 
+    // display items and entries in ID order 
+    // which effectively equates to add order.
+    function idsort(a, b) { return a.id() < b.id() ? -1 : 1 }
+
     if(invoice) {
         dojo.forEach(
-            invoice.items(),
+            invoice.items().sort(idsort),
             function(item) {
                 addInvoiceItem(item);
             }
         );
 
         dojo.forEach(
-            invoice.entries(),
+            invoice.entries().sort(idsort),
             function(entry) {
                 addInvoiceEntry(entry);
             }
@@ -137,10 +164,10 @@ function renderInvoice() {
     if(attachPo.length) doAttachPo(0);
 }
 
-function doAttachLi() {
+function doAttachLi(skipInit) {
 
     //var invoiceArgs = {provider : lineitem.provider(), shipper : lineitem.provider()}; 
-    if(cgi.param('create')) {
+    if(cgi.param('create') && !skipInit) {
 
         // use the first LI in the list to determine the default provider
         fieldmapper.standardRequest(
@@ -223,6 +250,172 @@ function doAttachPo(idx) {
     );
 }
 
+function performSearch(pageDir) {
+    clearSearchResTable(); 
+    var searchObject = termManager.buildSearchObject();
+    dojo.cookie('invs', base64Encode(searchObject));
+    dojo.cookie('invc', dojo.byId("acq-unified-conjunction").getValue());
+
+    if (pageDir == 0) { // new search
+        resultsLoader.displayOffset = 0;
+    } else {
+        resultsLoader.displayOffset += pageDir * resultsLoader.displayLimit;
+    }
+
+    if (resultsLoader.displayOffset == 0) {
+        openils.Util.hide('acq-inv-search-prev');
+    } else {
+        openils.Util.show('acq-inv-search-prev', 'inline');
+    }
+
+    if (dojo.byId('acq-invoice-search-limit-invoiceable').checked) {
+        if (!searchObject.jub) 
+            searchObject.jub = [];
+
+        // exclude lineitems that are "cancelled" (sidebar: 'Mericans spell it 'canceled')
+        searchObject.jub.push({state : 'cancelled', '__not' : true});
+
+        // exclude lineitems already linked to this invoice
+        if (invoice && invoice.id() > 0) { 
+            if (!searchObject.acqinv)
+                searchObject.acqinv = [];
+            searchObject.acqinv.push({id : invoice.id(), '__not' : true});
+        }
+
+        // limit to lineitems that have invoiceable copies
+        searchObject.acqlisumi = [{item_count : 1, '_gte' : true}];
+
+        // limit to provider if a provider is selected
+        var provider = invoicePane.getFieldValue('provider');
+        if (provider) {
+            if (!searchObject.jub.filter(function(i) { return i.provider != null }).length)
+                searchObject.jub.push({provider : provider});
+        }
+    }
+
+    if (dojo.byId('acq-invoice-search-sort-title').checked) {
+        uriManager.order_by = 
+            [ {"class": "acqlia", "field":"attr_value", "transform":"first"} ];
+    }
+
+    resultsLoader.lastSearch = searchObject;
+    resultManager.go(searchObject)
+    console.log('Lineitem Search: ' + js2JSON(searchObject));
+    focusLastSearchInput();
+}
+
+
+function renderUnifiedSearch() {
+
+    if (!searchInitDone) {
+
+        searchInitDone = true;
+        termManager = new TermManager();
+        resultManager = new ResultManager();
+        resultsLoader = new searchResultsLoader();
+        uriManager = new URIManager();
+
+        // define custom lineitem result handler
+        resultManager.result_types = {
+            "lineitem": {
+                "search_options": { "id_list": true },
+                "revealer": function() { },
+                "finisher": function() {
+                    resultsLoader.batch_length = resultManager.count_results;
+                },
+                "adder": function(li) {
+                    resultsLoader.addLineitem(li);
+                },
+                "interface": resultsLoader
+            },
+            "no_results": {
+                "revealer": function() { }
+            }
+        };
+
+        var searchObject = dojo.cookie('invs');
+        console.log('loaded ' + searchObject);
+        if (searchObject) {
+            // if there is a search object cookie, populate the search form
+            termManager.reflect(base64Decode(searchObject));
+            dojo.byId("acq-unified-conjunction").setValue(dojo.cookie('invc'));
+        } else {
+            console.log('adding row');
+            termManager.addRow();
+        }
+    }
+
+    dojo.addClass(dojo.byId('oils-acq-invoice-table'), 'hidden');
+    dojo.removeClass(dojo.byId('oils-acq-invoice-search'), 'hidden');
+    focusLastSearchInput();
+}
+
+function focusLastSearchInput() {
+    // TODO: see about making this better and moving it into search/unified.js
+    var wnodes = dojo.query('[name=widget]');
+    var inputNode = wnodes.item(wnodes.length - 1).firstChild;
+    if (inputNode) {
+        try {
+            inputNode.select();
+        } catch(E) {
+            inputNode.focus();
+        }
+    }
+}
+
+var resultsTbody, resultsRow;
+function searchResultsLoader() {
+    this.displayOffset = 0;
+    this.displayLimit = 10;
+
+    if (!resultsTbody) {
+        resultsTbody = dojo.byId('acq-invoice-search-results-tbody');
+        resultsRow = resultsTbody.removeChild(dojo.byId('acq-invoice-search-results-tr'));
+    }
+
+    this.addLineitem = function(li_id) {
+        console.log('Adding search result lineitem ' + li_id);
+        var row = resultsRow.cloneNode(true);
+        resultsTbody.appendChild(row);
+        var checkbox = dojo.query('[name=search-results-checkbox]', row)[0];
+        checkbox.setAttribute('lineitem', li_id);
+
+        // this lineitem is already part of the invoice
+        if (dojo.query('[entry_lineitem_row=' + li_id + ']')[0]) {
+            checkbox.disabled = true;
+            dojo.addClass(checkbox.parentNode, 'search-results-already-invoiced');
+        }
+
+        openils.acq.Lineitem.fetchAndRender(
+            li_id, {}, 
+            function(li, html) { 
+                dojo.query('[name=search-results-content-div]', row)[0].innerHTML = html;
+            }
+        );
+    }
+}
+
+function addSelectedToInvoice() {
+    var inputs = dojo.query('[name=search-results-checkbox]');
+    attachLi = [];
+    dojo.forEach(inputs,
+        function(checkbox) {
+            if (checkbox.checked) {
+                attachLi.push(checkbox.getAttribute('lineitem'));
+                checkbox.disabled = true;
+                checkbox.checked = false;
+                dojo.addClass(checkbox.parentNode, 'search-results-already-invoiced');
+            }
+        }
+    );
+    doAttachLi(true);
+}
+
+function clearSearchResTable() {
+    while (resultsTbody.childNodes[0])
+        resultsTbody.removeChild(resultsTbody.childNodes[0]);
+}
+
 function updateTotalCost() {
 
     var totalCost = 0;    
@@ -232,7 +425,7 @@ function updateTotalCost() {
     for(var id in widgetRegistry.acqie) 
         if(!widgetRegistry.acqie[id]._object.isdeleted())
             totalCost += Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue());
-    totalInvoicedBox.attr('value', totalCost);
+    totalInvoicedBox.innerHTML = totalCost.toFixed(2);
 
     totalPaid = 0;    
     for(var id in widgetRegistry.acqii) 
@@ -241,27 +434,27 @@ function updateTotalCost() {
     for(var id in widgetRegistry.acqie) 
         if(!widgetRegistry.acqie[id]._object.isdeleted())
             totalPaid += Number(widgetRegistry.acqie[id].amount_paid.getFormattedValue());
-    totalPaidBox.attr('value', totalPaid);
+    totalPaidBox.innerHTML = totalPaid.toFixed(2);
 
     var buttonsDisabled = false;
 
     if(totalPaid > totalCost || totalPaid < 0) {
-        openils.Util.addCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+        openils.Util.addCSSClass(totalPaidBox, 'acq-invoice-invalid-amount');
         invoiceSaveButton.attr('disabled', true);
         invoiceProrateButton.attr('disabled', true);
         buttonsDisabled = true;
     } else {
-        openils.Util.removeCSSClass(totalPaidBox.domNode, 'acq-invoice-invalid-amount');
+        openils.Util.removeCSSClass(totalPaidBox, 'acq-invoice-invalid-amount');
         invoiceSaveButton.attr('disabled', false);
         invoiceProrateButton.attr('disabled', false);
     }
 
     if(totalCost < 0) {
-        openils.Util.addCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+        openils.Util.addCSSClass(totalInvoicedBox, 'acq-invoice-invalid-amount');
         invoiceSaveButton.attr('disabled', true);
         invoiceProrateButton.attr('disabled', true);
     } else {
-        openils.Util.removeCSSClass(totalInvoicedBox.domNode, 'acq-invoice-invalid-amount');
+        openils.Util.removeCSSClass(totalInvoicedBox, 'acq-invoice-invalid-amount');
         if(!buttonsDisabled) {
             invoiceSaveButton.attr('disabled', false);
             invoiceProrateButton.attr('disabled', false);
@@ -274,7 +467,9 @@ function updateTotalCost() {
         invoiceCloseButton.attr('disabled', true);
     }
 
-    balanceOwedBox.attr('value', (totalCost - totalPaid));
+    balanceOwedBox.innerHTML = (totalCost - totalPaid).toFixed(2);
+
+    updateExpectedCost();
 }
 
 
@@ -316,6 +511,7 @@ function addInvoiceItem(item) {
             } else if(field == 'cost_billed' || field == 'amount_paid') {
                 args = {required : true, style : 'width: 8em'};
             }
+
             registerWidget(
                 item,
                 field,
@@ -481,12 +677,39 @@ function focusLi() {
 }
 
 
+// expected cost is totalCostInvoiced + totalCostNotYetInvoiced
+function updateExpectedCost() {
+
+    var cost = Number(totalInvoicedBox.innerHTML || 0);
+
+    // for any LI's that are not yet billed (i.e. filled in)
+    // use the total expected cost for that lineitem.
+    for(var id in widgetRegistry.acqie) {
+        var entry = widgetRegistry.acqie[id]._object;
+        if(!entry.isdeleted()) {
+            if (Number(widgetRegistry.acqie[id].cost_billed.getFormattedValue()) == 0) {
+                var li = entry.lineitem();
+                cost += 
+                    Number(li.order_summary().estimated_amount()) - 
+                    Number(li.order_summary().paid_amount());
+            }
+        }
+    }
+
+    dojo.byId('acq-invoice-summary-cost').innerHTML = cost.toFixed(2);
+}
+
+var invoicEntryWidgets = {};
 function addInvoiceEntry(entry) {
+    console.log('Adding new entry for lineitem ' + entry.lineitem());
 
     openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-header'), 'hidden');
     openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-thead'), 'hidden');
     openils.Util.removeCSSClass(dojo.byId('acq-invoice-entry-tbody'), 'hidden');
 
+    dojo.byId('acq-invoice-summary-count').innerHTML = 
+        Number(dojo.byId('acq-invoice-summary-count').innerHTML) + 1;
+
     entryTbody = dojo.byId('acq-invoice-entry-tbody');
     if(entryTemplate == null) {
         entryTemplate = entryTbody.removeChild(dojo.byId('acq-invoice-entry-template'));
@@ -498,6 +721,7 @@ function addInvoiceEntry(entry) {
 
     var row = entryTemplate.cloneNode(true);
     row.setAttribute('lineitem', entry.lineitem());
+    row.setAttribute('entry_lineitem_row', entry.lineitem());
 
     openils.acq.Lineitem.fetchAndRender(
         entry.lineitem(), {}, 
@@ -511,6 +735,14 @@ function addInvoiceEntry(entry) {
 
             updateReceiveLink(li);
 
+            // set some default values if otherwise unset
+            if (!invoicePane.getFieldValue('receiver')) {
+                invoicePane.setFieldValue('receiver', li.purchase_order().ordering_agency());
+            }
+            if (!invoicePane.getFieldValue('provider')) {
+                invoicePane.setFieldValue('provider', li.purchase_order().provider());
+            }
+
             dojo.forEach(
                 ['inv_item_count', 'phys_item_count', 'cost_billed', 'amount_paid'],
                 function(field) {
@@ -536,6 +768,7 @@ function addInvoiceEntry(entry) {
                             parentNode : nodeByName(field, row)
                         }),
                         function(w) {    
+
                             if(field == 'phys_item_count') {
                                 dojo.connect(w, 'onChange', 
                                     function() {
@@ -548,12 +781,20 @@ function addInvoiceEntry(entry) {
                                         }
                                     }
                                 )
-                            }
-                        }
+                            } // if
+
+                            if(field == 'inv_item_count' || field == 'cost_billed') {
+                                setPerCopyPrice(row, entry);
+                                // update the per-copy count as invoice count and cost billed change 
+                                dojo.connect(w, 'onChange', function() { setPerCopyPrice(row, entry) } );
+                            } 
+
+                        } // func
                     );
                 }
             );
 
+            updateTotalCost();
             if (focusLineitem == li.id())
                 focusLi();
         }
@@ -586,7 +827,22 @@ function addInvoiceEntry(entry) {
     }
 
     entryTbody.appendChild(row);
-    updateTotalCost();
+}
+
+function setPerCopyPrice(row, entry) {
+    var inv_w = widgetRegistry.acqie[entry.id()].inv_item_count;
+    var bill_w = widgetRegistry.acqie[entry.id()].cost_billed;
+
+    if (inv_w && bill_w) {
+        var invoiced = Number(inv_w.getFormattedValue());
+        var billed = Number(bill_w.getFormattedValue());
+        console.log(invoiced + ' : ' + billed);
+        if (invoiced > 0) {
+            nodeByName('amount_paid_per_copy', row).innerHTML = (billed / invoiced).toFixed(2);
+        } else {
+            nodeByName('amount_paid_per_copy', row).innerHTML = '0.00';
+        }
+    }
 }
 
 function liMarcAttr(lineitem, name) {
@@ -601,12 +857,9 @@ function liMarcAttr(lineitem, name) {
     return (attr) ? attr.attr_value() : '';
 }
 
-function saveChanges(doProrate, doClose, doReopen) {
-    createExtraCopies(
-        function() {
-            saveChangesPartTwo(doProrate, doClose, doReopen);
-        }
-    );
+function saveChanges(args) {
+    args = args || {};
+    createExtraCopies(function() { saveChangesPartTwo(args); });
 }
 
 // Define a helper function to 'unflesh' sub-objects from an fmclass object.
@@ -621,10 +874,10 @@ function unflesh() {
     });
 }
 
-function saveChangesPartTwo(doProrate, doClose, doReopen) {
-    
+function saveChangesPartTwo(args) {
+    args = args || {};
 
-    if(doReopen) {
+    if(args.reopen) {
         invoice.complete('f');
 
     } else {
@@ -643,7 +896,7 @@ function saveChangesPartTwo(doProrate, doClose, doReopen) {
             return;
         }
 
-        if(doClose)
+        if(args.close)
             invoice.complete('t');
 
 
@@ -693,9 +946,13 @@ function saveChangesPartTwo(doProrate, doClose, doReopen) {
                 progressDialog.hide();
                 var invoice = openils.Util.readResponse(r);
                 if(invoice) {
-                    if(doProrate)
+                    if(args.prorate)
                         return prorateInvoice(invoice);
-                    location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+                    if (args.clear) {
+                        location.href = oilsBasePath + '/acq/invoice/view?create=1';
+                    } else {
+                        location.href = oilsBasePath + '/acq/invoice/view/' + invoice.id();
+                    }
                 }
             }
         }
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 b7f0137..2c1eff0 100644
--- a/Open-ILS/web/js/ui/default/acq/search/unified.js
+++ b/Open-ILS/web/js/ui/default/acq/search/unified.js
@@ -4,6 +4,7 @@ dojo.require("openils.widget.AutoGrid");
 dojo.require("openils.widget.AutoWidget");
 dojo.require("openils.widget.XULTermLoader");
 dojo.require("openils.PermaCrud");
+dojo.require('dijit.layout.TabContainer');
 
 if (!localeStrings) {   /* we can do this because javascript doesn't have block 
                            scope */
@@ -251,7 +252,7 @@ function TermManager() {
     };
 
     this.terms = {};
-    ["jub", "acqpl", "acqpo", "acqinv"].forEach(
+    ["jub", "acqpl", "acqpo", "acqinv", "acqlid"].forEach(
         function(hint) {
             var o = {};
             o.__label = fieldmapper.IDL.fmclasses[hint].label;
@@ -621,84 +622,91 @@ function ResultManager(liPager, poGrid, plGrid, invGrid) {
     this.plCache = {};
     this.invCache = {};
 
-    this.result_types = {
-        "lineitem": {
-            "search_options": {
-                "flesh_attrs": true,
-                "flesh_cancel_reason": true,
-                "flesh_notes": true
-            },
-            "revealer": function() {
-                self.liPager.show();
-                progressDialog.show(true);
-            },
-            "finisher": function() {
-                self.liPager.batch_length = self.count_results;
-                self.liPager.relabelControls();
-                self.liPager.enableControls(true);
-                progressDialog.hide();
-            },
-            "adder": function(li) {
-                self.liPager.liTable.addLineitem(li);
-            },
-            "interface": self.liPager
-        },
-        "purchase_order": {
-            "search_options": {
-                "no_flesh_cancel_reason": true
-            },
-            "revealer": function() {
-                self.poGrid.resetStore();
-                self.poGrid.showLoadProgressIndicator();
-                self.poCache = {};
-            },
-            "finisher": function() {
-                self.poGrid.hideLoadProgressIndicator();
-            },
-            "adder": function(po) {
-                self.poCache[po.id()] = po;
-                self.poGrid.store.newItem(acqpo.toStoreItem(po));
-            },
-            "interface": self.poGrid
-        },
-        "picklist": {
-            "search_options": {
-                "flesh_lineitem_count": true,
-                "flesh_owner": true
-            },
-            "revealer": function() {
-                self.plGrid.resetStore();
-                self.plGrid.showLoadProgressIndicator();
-                self.plCache = {};
-            },
-            "finisher": function() {
-                self.plGrid.hideLoadProgressIndicator();
-            },
-            "adder": function(pl) {
-                self.plCache[pl.id()] = pl;
-                self.plGrid.store.newItem(acqpl.toStoreItem(pl));
-            },
-            "interface": self.plGrid
-        },
-        "invoice": {
-            "search_options": {
-                "no_flesh_misc": true
+    if (window.unifiedSearchExternalMode) {
+
+        // external user will define result types and handlers
+
+    } else {
+
+        this.result_types = {
+            "lineitem": {
+                "search_options": {
+                    "flesh_attrs": true,
+                    "flesh_cancel_reason": true,
+                    "flesh_notes": true
+                },
+                "revealer": function() {
+                    self.liPager.show();
+                    progressDialog.show(true);
+                },
+                "finisher": function() {
+                    self.liPager.batch_length = self.count_results;
+                    self.liPager.relabelControls();
+                    self.liPager.enableControls(true);
+                    progressDialog.hide();
+                },
+                "adder": function(li) {
+                    self.liPager.liTable.addLineitem(li);
+                },
+                "interface": self.liPager
             },
-            "finisher": function() {
-                self.invGrid.hideLoadProgressIndicator();
+            "purchase_order": {
+                "search_options": {
+                    "no_flesh_cancel_reason": true
+                },
+                "revealer": function() {
+                    self.poGrid.resetStore();
+                    self.poGrid.showLoadProgressIndicator();
+                    self.poCache = {};
+                },
+                "finisher": function() {
+                    self.poGrid.hideLoadProgressIndicator();
+                },
+                "adder": function(po) {
+                    self.poCache[po.id()] = po;
+                    self.poGrid.store.newItem(acqpo.toStoreItem(po));
+                },
+                "interface": self.poGrid
             },
-            "revealer": function() {
-                self.invGrid.resetStore();
-                self.invCache = {};
+            "picklist": {
+                "search_options": {
+                    "flesh_lineitem_count": true,
+                    "flesh_owner": true
+                },
+                "revealer": function() {
+                    self.plGrid.resetStore();
+                    self.plGrid.showLoadProgressIndicator();
+                    self.plCache = {};
+                },
+                "finisher": function() {
+                    self.plGrid.hideLoadProgressIndicator();
+                },
+                "adder": function(pl) {
+                    self.plCache[pl.id()] = pl;
+                    self.plGrid.store.newItem(acqpl.toStoreItem(pl));
+                },
+                "interface": self.plGrid
             },
-            "adder": function(inv) {
-                self.invCache[inv.id()] = inv;
-                self.invGrid.store.newItem(acqinv.toStoreItem(inv));
+            "invoice": {
+                "search_options": {
+                    "no_flesh_misc": true
+                },
+                "finisher": function() {
+                    self.invGrid.hideLoadProgressIndicator();
+                },
+                "revealer": function() {
+                    self.invGrid.resetStore();
+                    self.invCache = {};
+                },
+                "adder": function(inv) {
+                    self.invCache[inv.id()] = inv;
+                    self.invGrid.store.newItem(acqinv.toStoreItem(inv));
+                },
+                "interface": self.invGrid
             },
-            "interface": self.invGrid
-        },
-        "no_results": {
-            "revealer": function() { alert(localeStrings.NO_RESULTS); }
+            "no_results": {
+                "revealer": function() { alert(localeStrings.NO_RESULTS); }
+            }
         }
     };
 
@@ -788,10 +796,23 @@ function ResultManager(liPager, poGrid, plGrid, invGrid) {
     };
 
     this.go = function(search_object) {
+
+        if (window.unifiedSearchExternalMode) {
+            // assume for now that external mode implies inline results display
+            
+            uriManager = uriManager || new URIManager();
+            uriManager.search_object = search_object;
+            uriManager.result_type = dojo.byId("acq-unified-result-type").getValue();
+            uriManager.conjunction = dojo.byId("acq-unified-conjunction").getValue();
+            this.search(uriManager, termManager);
+
+        } else {
+
         location.href = oilsBasePath + "/acq/search/unified?" +
             "so=" + base64Encode(search_object) +
             "&rt=" + dojo.byId("acq-unified-result-type").getValue() +
             "&c=" + dojo.byId("acq-unified-conjunction").getValue();
+        }
     };
 
     this.search = function(uriManager, termManager) {
@@ -928,7 +949,12 @@ function URIManager() {
 /* onload */
 openils.Util.addOnLoad(
     function() {
+
+        // onload handled by external user
+        if (window.unifiedSearchExternalMode) return;
+
         termManager = new TermManager();
+
         resultManager = new ResultManager(
             new LiTablePager(null, new AcqLiTable()),
             dijit.byId("acq-unified-po-grid"),
diff --git a/docs/RELEASE_NOTES_NEXT/acq-invoice-li-search.txt b/docs/RELEASE_NOTES_NEXT/acq-invoice-li-search.txt
new file mode 100644
index 0000000..3ef304d
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/acq-invoice-li-search.txt
@@ -0,0 +1,22 @@
+ACQ Invoice Inline Lineitem Search and Add
+------------------------------------------
+
+The Invoice UI is how composed of two tabs, the main invoice tab and a new Search tab.  The search tab consists of a subset of the Acquisitions unified search interface.  The goal is to allow users to search for lineitems to invoice.  Search results may be added directly to the growing invoice.  A number of small usability features are included.
+
+Features
+~~~~~~~~
+
+    * Option (default) to limit searches to invoiceable items.  
+        ** These are lineitems that are not cancelled, have at least one invoiceable copy, linked to a PO whose provider matches that of the current invoice, and are not already linked to the current invoice.
+    * Search defaults to last-run search (on workstation).
+    * New Lineitem Detail filter options
+    * Sort searches by lineitem number (default) and title.
+    * There is a new Expected Cost field which includes both the total invoiced cost plus the anticipated cost of lineitems as they are added.
+    * New Price per Copy field
+    * Lineitem count field
+    * Show / Hide Invoice details button.  Details are displayed by default, but hidden when the user enters the search tab.  From there it remains hidden until manually shown (or a new invoice is opened).
+    * A new "Save & Clear" button which saves the current invoice then clears the invoice display to create a new invoice.
+    * Provider, shipper, and receiver fields are auto-populated from the first-added invoice data (when not already set).
+    * Totals are now read-only, since they are derived from existing data (and are informational only).
+
+

commit f502f9e4265603e47f9747d4dc30ee73c26dac07
Author: Bill Erickson <berick at esilibrary.com>
Date:   Fri Jul 20 13:53:35 2012 -0400

    EditPane setFieldValue method
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/web/js/dojo/openils/widget/EditPane.js b/Open-ILS/web/js/dojo/openils/widget/EditPane.js
index 887015d..6876dd3 100644
--- a/Open-ILS/web/js/dojo/openils/widget/EditPane.js
+++ b/Open-ILS/web/js/dojo/openils/widget/EditPane.js
@@ -237,6 +237,15 @@ if(!dojo._hasResource['openils.widget.EditPane']) {
                 }
             },
 
+            setFieldValue : function(field, val) {
+                for(var i in this.fieldList) {
+                    if(field == this.fieldList[i].name) {
+                        this.fieldList[i].widget.widget.attr('value', val);
+                    }
+                }
+            },
+
+
             performAutoEditAction : function() {
                 var self = this;
                 self.performEditAction({

commit 07ba14c420cfda48dc550cf0888fd96d29603e76
Author: Bill Erickson <berick at esilibrary.com>
Date:   Fri Jul 20 11:15:25 2012 -0400

    EditPane.js API repairs
    
    By default, don't throw an exception from EditPane.getFieldValue when a
    field is required and the value is null.  Otherwise, external code is
    unable to use this method for inspecting the pane.  Make checkRequired
    an additional method flag.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/web/js/dojo/openils/widget/EditPane.js b/Open-ILS/web/js/dojo/openils/widget/EditPane.js
index d7c54b8..887015d 100644
--- a/Open-ILS/web/js/dojo/openils/widget/EditPane.js
+++ b/Open-ILS/web/js/dojo/openils/widget/EditPane.js
@@ -222,15 +222,17 @@ if(!dojo._hasResource['openils.widget.EditPane']) {
                 }
             },
 
-            getFieldValue : function(field) {
+            getFieldValue : function(field, checkRequired) {
                 for(var i in this.fieldList) {
                     if(field == this.fieldList[i].name) {
                         var val = this.fieldList[i].widget.getFormattedValue();
-                        if (val == null && /* XXX stricter check needed? */
+                        if (checkRequired &&
+                            val == null && /* XXX stricter check needed? */
                             this.fieldList[i].widget.isRequired()) {
                             throw new Error("req");
                         }
                         return val;
+
                     }
                 }
             },
@@ -253,7 +255,7 @@ if(!dojo._hasResource['openils.widget.EditPane']) {
                 try {
                     for(var idx in fields) {
                         this.fmObject[fields[idx]](
-                            this.getFieldValue(fields[idx])
+                            this.getFieldValue(fields[idx], true)
                         );
                     }
                 } catch (E) {

commit 7b12ae81a56b1d998eb6c9f239ef259d3e23027a
Author: Bill Erickson <berick at esilibrary.com>
Date:   Fri Jul 20 10:25:39 2012 -0400

    ACQ lineitem summary moved to real DB view
    
    This is allows us to create new IDL views based on the lineitem summary.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Lebbeous Fogle-Weekley <lebbeous at esilibrary.com>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 243f918..7170638 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -9654,58 +9654,26 @@ SELECT  usr,
 			<link field="claim_policy_action" reltype="has_a" key="id" map="" class="acqclpa"/>
 		</links>
 	</class>
-
-	<class id="acqlisum" controller="open-ils.cstore" oils_obj:fieldmapper="acq::lineitem_summary" oils_persist:readonly="true" reporter:label="Lineitem Summary">
+	<class id="acqlisum" controller="open-ils.cstore" oils_obj:fieldmapper="acq::lineitem_summary" oils_persist:readonly="true" reporter:label="Lineitem Summary" oils_persist:tablename="acq.lineitem_summary">
+		<fields oils_persist:primary="lineitem" oils_persist:sequence="acq.lineitem_id_seq">
+			<field reporter:label="Lineitem" name="lineitem" reporter:datatype="link"/>
+			<field reporter:label="Item Count" name="item_count" reporter:datatype="int"/>
+			<field reporter:label="Receive Count" name="recv_count" reporter:datatype="int"/>
+			<field reporter:label="Cancel Count" name="cancel_count" reporter:datatype="int"/>
+			<field reporter:label="Invoice Count" name="invoice_count" reporter:datatype="int"/>
+			<field reporter:label="Claim Count" name="claim_count" reporter:datatype="int"/>
+			<field reporter:label="Estimated Amount" name="estimated_amount" reporter:datatype="money"/>
+			<field reporter:label="Encumbrance Amount" name="encumbrance_amount" reporter:datatype="money"/>
+			<field reporter:label="Paid Amount" name="paid_amount" reporter:datatype="money"/>
+		</fields>
+		<links>
+			<link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
+		</links>
+    </class>
+	<class id="acqlisumi" controller="open-ils.cstore" oils_obj:fieldmapper="acq::lineitem_summary_invoiceable" oils_persist:readonly="true" reporter:label="Invoiceable Lineitem Summary">
 		<oils_persist:source_definition>
-
-            SELECT 
-                li.id AS lineitem, 
-                (
-                    SELECT COUNT(lid.id) 
-                    FROM acq.lineitem_detail lid
-                    WHERE lineitem = li.id
-                ) AS item_count,
-                (
-                    SELECT COUNT(lid.id) 
-                    FROM acq.lineitem_detail lid
-                    WHERE recv_time IS NOT NULL AND lineitem = li.id
-                ) AS recv_count,
-                (
-                    SELECT COUNT(lid.id) 
-                    FROM acq.lineitem_detail lid
-                    WHERE cancel_reason IS NOT NULL AND lineitem = li.id
-                ) AS cancel_count,
-                (
-                    SELECT COUNT(lid.id) 
-                    FROM acq.lineitem_detail lid
-                        JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
-                    WHERE NOT debit.encumbrance AND lineitem = li.id
-                ) AS invoice_count,
-                (
-                    SELECT COUNT(DISTINCT(lid.id)) 
-                    FROM acq.lineitem_detail lid
-                        JOIN acq.claim claim ON (claim.lineitem_detail = lid.id)
-                    WHERE lineitem = li.id
-                ) AS claim_count,
-                (
-                    SELECT (COUNT(lid.id) * li.estimated_unit_price)::NUMERIC(8,2)
-                    FROM acq.lineitem_detail lid
-                    WHERE lid.cancel_reason IS NULL AND lineitem = li.id
-                ) AS estimated_amount,
-                (
-                    SELECT SUM(debit.amount)::NUMERIC(8,2)
-                    FROM acq.lineitem_detail lid
-                        JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
-                    WHERE debit.encumbrance AND lineitem = li.id
-                ) AS encumbrance_amount,
-                (
-                    SELECT SUM(debit.amount)::NUMERIC(8,2)
-                    FROM acq.lineitem_detail lid
-                        JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
-                    WHERE NOT debit.encumbrance AND lineitem = li.id
-                ) AS paid_amount
-
-                FROM acq.lineitem AS li
+            SELECT * FROM acq.lineitem_summary 
+				WHERE item_count > (invoice_count + cancel_count)
         </oils_persist:source_definition>
 		<fields oils_persist:primary="lineitem" oils_persist:sequence="acq.lineitem_id_seq">
 			<field reporter:label="Lineitem" name="lineitem" reporter:datatype="link"/>
@@ -9722,8 +9690,6 @@ SELECT  usr,
 			<link field="lineitem" reltype="has_a" key="id" map="" class="jub"/>
 		</links>
     </class>
-
-
 	<class id="iatc" controller="open-ils.reporter-store" oils_obj:fieldmapper="action::intersystem_transit_copy" oils_persist:readonly="true" reporter:core="true" reporter:label="Inter-system Copy Transit">
 		<oils_persist:source_definition>
 
diff --git a/Open-ILS/src/sql/Pg/200.schema.acq.sql b/Open-ILS/src/sql/Pg/200.schema.acq.sql
index c84279b..aad60a4 100644
--- a/Open-ILS/src/sql/Pg/200.schema.acq.sql
+++ b/Open-ILS/src/sql/Pg/200.schema.acq.sql
@@ -2418,4 +2418,54 @@ CREATE TABLE acq.serial_claim_event (
 
 CREATE INDEX serial_claim_event_claim_date_idx ON acq.serial_claim_event( claim, event_date );
 
+CREATE OR REPLACE VIEW acq.lineitem_summary AS
+    SELECT 
+        li.id AS lineitem, 
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE lineitem = li.id
+        ) AS item_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE recv_time IS NOT NULL AND lineitem = li.id
+        ) AS recv_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE cancel_reason IS NOT NULL AND lineitem = li.id
+        ) AS cancel_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS invoice_count,
+        (
+            SELECT COUNT(DISTINCT(lid.id)) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.claim claim ON (claim.lineitem_detail = lid.id)
+            WHERE lineitem = li.id
+        ) AS claim_count,
+        (
+            SELECT (COUNT(lid.id) * li.estimated_unit_price)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+            WHERE lid.cancel_reason IS NULL AND lineitem = li.id
+        ) AS estimated_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE debit.encumbrance AND lineitem = li.id
+        ) AS encumbrance_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS paid_amount
+
+        FROM acq.lineitem AS li;
+
 COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-lineitem-summary.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-lineitem-summary.sql
new file mode 100644
index 0000000..31b4c60
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-lineitem-summary.sql
@@ -0,0 +1,53 @@
+BEGIN;
+
+CREATE OR REPLACE VIEW acq.lineitem_summary AS
+    SELECT 
+        li.id AS lineitem, 
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE lineitem = li.id
+        ) AS item_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE recv_time IS NOT NULL AND lineitem = li.id
+        ) AS recv_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+            WHERE cancel_reason IS NOT NULL AND lineitem = li.id
+        ) AS cancel_count,
+        (
+            SELECT COUNT(lid.id) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS invoice_count,
+        (
+            SELECT COUNT(DISTINCT(lid.id)) 
+            FROM acq.lineitem_detail lid
+                JOIN acq.claim claim ON (claim.lineitem_detail = lid.id)
+            WHERE lineitem = li.id
+        ) AS claim_count,
+        (
+            SELECT (COUNT(lid.id) * li.estimated_unit_price)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+            WHERE lid.cancel_reason IS NULL AND lineitem = li.id
+        ) AS estimated_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE debit.encumbrance AND lineitem = li.id
+        ) AS encumbrance_amount,
+        (
+            SELECT SUM(debit.amount)::NUMERIC(8,2)
+            FROM acq.lineitem_detail lid
+                JOIN acq.fund_debit debit ON (lid.fund_debit = debit.id)
+            WHERE NOT debit.encumbrance AND lineitem = li.id
+        ) AS paid_amount
+
+        FROM acq.lineitem AS li;
+
+COMMIT;

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

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |   72 +---
 .../perlmods/lib/OpenILS/Application/Acq/Search.pm |   46 ++-
 Open-ILS/src/sql/Pg/200.schema.acq.sql             |   50 +++
 .../upgrade/XXXX.schema.acq-lineitem-summary.sql   |   53 +++
 Open-ILS/src/templates/acq/invoice/view.tt2        |  403 ++++++++++++++------
 Open-ILS/web/css/skin/default/acq.css              |    2 +-
 Open-ILS/web/js/dojo/openils/widget/EditPane.js    |   17 +-
 Open-ILS/web/js/ui/default/acq/invoice/common.js   |   26 ++-
 Open-ILS/web/js/ui/default/acq/invoice/view.js     |  315 ++++++++++++++--
 Open-ILS/web/js/ui/default/acq/search/unified.js   |  194 ++++++----
 docs/RELEASE_NOTES_NEXT/acq-invoice-li-search.txt  |   22 +
 11 files changed, 925 insertions(+), 275 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.schema.acq-lineitem-summary.sql
 create mode 100644 docs/RELEASE_NOTES_NEXT/acq-invoice-li-search.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list