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

Evergreen Git git at git.evergreen-ils.org
Wed Aug 24 18:33:40 EDT 2016


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  15ad6d45c3066c3e88c6d6e0c28b2f445a3132b0 (commit)
       via  3f2d3a0335fe935e4ec3a310b018b76cdd9f7e3f (commit)
       via  57697077ec3a4431c16d043d1d9270c3e18c1fba (commit)
       via  b9e5c623f2cde86b1b87d07ba9e4a637f1b3ac5e (commit)
       via  11d7f2206e4583db64626f9761c00c7746242cbe (commit)
       via  52bf3fe8e791c43448c49fa9818f5790625c724c (commit)
      from  8decad6183c9c3786377bd59eae8c55472ab35c4 (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 15ad6d45c3066c3e88c6d6e0c28b2f445a3132b0
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Aug 24 18:32:02 2016 -0400

    Stamping upgrade scripts for aged circs display branch
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index d696187..ca947ce 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0997', :eg_version); -- csharp/miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0998', :eg_version); -- berick/miker
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql b/Open-ILS/src/sql/Pg/upgrade/0998.schema.aged-circ-chains.sql
similarity index 98%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql
rename to Open-ILS/src/sql/Pg/upgrade/0998.schema.aged-circ-chains.sql
index b618980..8f6a213 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0998.schema.aged-circ-chains.sql
@@ -1,7 +1,7 @@
 
 BEGIN;
 
--- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0998', :eg_version);
 
 DROP VIEW IF EXISTS action.all_circulation;
 CREATE VIEW action.all_circulation AS

commit 3f2d3a0335fe935e4ec3a310b018b76cdd9f7e3f
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Aug 24 17:01:01 2016 -0400

    LP#1497335 Show Last Few Circs patron retrieve options
    
    In the Show Last Few Circulations window (XUL), disable the "Retrieve
    Last Patron" button when the most recent circulation is aged.  Disable
    the "Retrieve All These Patrons" button when all circulations displayed
    are aged.
    
    Fixes a sort bug in open-ils.circ.copy_checkout_history.retrieve.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index 530da64..4d9f881 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -791,7 +791,7 @@ sub view_circs {
 
     return $e->search_action_all_circulation([
         {target_copy => $copyid}, 
-        {limit => $count, order_by => { circ => "xact_start DESC" }} 
+        {limit => $count, order_by => { combcirc => "xact_start DESC" }} 
     ]);
 }
 
diff --git a/Open-ILS/xul/staff_client/server/circ/circ_summary.xul b/Open-ILS/xul/staff_client/server/circ/circ_summary.xul
index 26c6e07..0c46e99 100644
--- a/Open-ILS/xul/staff_client/server/circ/circ_summary.xul
+++ b/Open-ILS/xul/staff_client/server/circ/circ_summary.xul
@@ -75,6 +75,8 @@
             }
         }
 
+        var newest_circ_aged = true;
+        var all_circs_aged = true;
         function show_circs() {
             try {
                 $('r_last').disabled = true; $('r_all').disabled = true;    
@@ -87,6 +89,12 @@
 
                 var gb = $('circs');
                 for (var j = 0; j < g.circs.length; j++) {
+                    if (g.circs[j].usr()) { // have at least 1 active circ
+                        all_circs_aged = false;
+                        if (j == 0) { // first circ is active
+                            newest_circ_aged = false;
+                        }
+                    }
                     var iframe = document.createElement('iframe');
                     iframe.setAttribute('style','overflow: none; min-height: 100px;');
                     iframe.setAttribute('flex','1');
@@ -94,6 +102,12 @@
                     iframe.setAttribute('src', urls.XUL_CIRC_BRIEF); // + '?circ_id=' + g.circs[j].id() );
                     get_contentWindow(iframe).xulG = { 'circ_id' : g.circs[j].id() };
                 }
+
+                // Disable retrieve patron buttons when there are no
+                // patrons to retrieve, because related circs are aged.
+                $('r_all').disabled = all_circs_aged;
+                $('r_last').disabled = newest_circ_aged;
+
             } catch(E) {
                 g.error.standard_unexpected_error_alert('error showing circs',E);
             }

commit 57697077ec3a4431c16d043d1d9270c3e18c1fba
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Aug 3 15:18:59 2016 -0400

    LP#1497335 Aged circ display release notes
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/docs/RELEASE_NOTES_NEXT/Circulation/aged-circs-see-light-of-day.adoc b/docs/RELEASE_NOTES_NEXT/Circulation/aged-circs-see-light-of-day.adoc
new file mode 100644
index 0000000..b5f9157
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Circulation/aged-circs-see-light-of-day.adoc
@@ -0,0 +1,21 @@
+Staff Client Honors Aged Circulations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The browser and XUL clients now better represent copy checkout history 
+by honoring and displaying information from aged circulations.  
+
+ * Browser client 'Recent Circ History' and the analogous XUL client 
+   'Circulation History' tabs show summary data for aged circulations
+   as well as regular/active circulations.  When aged circulation data
+   is displayed, any references to patron names are replaced by the string
+   "<Aged Circulation>".
+
+ * Browser client 'Circ History List' and the analogous XUL client 
+   'Last Few Circulations' tabs behave as above, plus their 'Add 
+   Billing' buttons are disabled when displaying aged circulation data.
+
+ * XUL client 'Retrieve Last Patron' actions from various UI's report, 
+   "Item XXX circulation is an aged circulation and has no linked user".
+   Browser client analog uses 'Circ History List' instead, no additional
+   changes required.
+

commit b9e5c623f2cde86b1b87d07ba9e4a637f1b3ac5e
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Aug 3 14:45:39 2016 -0400

    LP#1497335 Item details shows aged circs (XUL edition)
    
    * Item Status -> Circulation History now displays aged circulations as
      appropriate, showing "<Aged Circulation>" in lieu the patron's name.
    
    * Item Status (and others) -> Show Last Few Circulations, ditto above.
      Also, the 'Add Billing' button is disabled when displayed with an aged
      circulation row.
    
    * Copy -> Retrieve Last Patron action now includes aged circulations
      when looking for the most recent circulation.  When the most recent
      circ is an aged circ, the UI reports "Item XXX circulation is an aged
      circulation and has no linked user".  Among other things, this means
      the 3rd from last circ will not be confused as the 2nd to last circ
      when the 2nd to last is aged.
    
    * Mark Item Damaged will warn if the circ in question is aged.  This is
      just a sanity check and should never happen in reality, since the UI
      only supports this option on checked out items (i.e. active circs).
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/xul/staff_client/server/cat/util.js b/Open-ILS/xul/staff_client/server/cat/util.js
index 2a7578e..f7da886 100644
--- a/Open-ILS/xul/staff_client/server/cat/util.js
+++ b/Open-ILS/xul/staff_client/server/cat/util.js
@@ -418,6 +418,20 @@ cat.util.mark_item_damaged = function(copy_ids) {
                                     /* short-circuit this behavior.  We don't want to mark an item damaged and still have it circulating.  At least for now.  Wait until someone asks for it. */
                                     auto_checkin = true; 
                                     JSAN.use('patron.util');
+
+                                    if (!my_circ.usr()) {
+                                        // Since we are looking at copies that are still checked out,
+                                        // we should never get here.  Best to be safe, though.
+                                        alert(
+                                            document.getElementById('catStrings')
+                                            .getFormattedString(
+                                                'staff.cat.util.mark_item_damaged.item_no_linked_patron',
+                                                copies[i].barcode()
+                                            )
+                                        );
+                                        continue;
+                                    }
+
                                     var patron_obj = patron.util.retrieve_fleshed_au_via_id( ses(), my_circ.usr() );
                                     var patron_name = patron.util.format_name( patron_obj ) + ' : ' + patron_obj.card().barcode();
                                     var msg = $("catStrings").getFormattedString('staff.cat.util.mark_item_damaged.item_circulating_to_patron', [ 
diff --git a/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js b/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js
index 43c52ff..17abb99 100644
--- a/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js
+++ b/Open-ILS/xul/staff_client/server/circ/alternate_copy_summary.js
@@ -448,17 +448,26 @@ function load_item() {
             set("stop_fines", details.circ.stop_fines()); 
             set("stop_fines_time", util.date.formatted_date( details.circ.stop_fines_time(), '%{localized}' )); 
             set("target_copy", details.circ.target_copy()); 
-            set("circ_usr", details.circ.usr()); 
-            network.simple_request('FM_AU_FLESHED_RETRIEVE_VIA_ID',[ ses(), details.circ.usr() ], function(preq) {
-                var r_au = preq.getResultObject();
-                JSAN.use('patron.util');
-                set(
-                    'patron_name', 
-                    patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode(),
-                    details.circ.usr()
-                );
+
+            if (details.circ.usr()) {
+                set("circ_usr", details.circ.usr()); 
+                network.simple_request('FM_AU_FLESHED_RETRIEVE_VIA_ID',
+                    [ ses(), details.circ.usr() ], function(preq) {
+                    var r_au = preq.getResultObject();
+                    JSAN.use('patron.util');
+                    set(
+                        'patron_name', 
+                        patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode(),
+                        details.circ.usr()
+                    );
+                    set_tooltip('patron_name','circ id ' + details.circ.id());
+                });
+            } else {
+                set("circ_usr", "");
+                set('patron_name', document.getElementById(
+                    'circStrings').getString('staff.circ.aged_circ'));
                 set_tooltip('patron_name','circ id ' + details.circ.id());
-            });
+            }
             set("xact_finish", util.date.formatted_date( details.circ.xact_finish(), '%{localized}' )); 
             set("xact_start", util.date.formatted_date( details.circ.xact_start(), '%{localized}' )); 
             set("create_time", util.date.formatted_date( details.circ.create_time(), '%{localized}' )); 
@@ -491,16 +500,22 @@ function load_item() {
                     var robj = req.getResultObject();
                     if (!robj || typeof robj == 'null') { return; }
                     var summary = robj['summary'];
-                    network.simple_request('FM_AU_FLESHED_RETRIEVE_VIA_ID',[ ses(), robj['usr'] ], function(preq) {
-                        var r_au = preq.getResultObject();
-                        JSAN.use('patron.util');
-                        set(
-                            'prev_patron_name', 
-                            patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode(),
-                            robj['usr']
-                        );
+                    if (robj['usr']) {
+                        network.simple_request('FM_AU_FLESHED_RETRIEVE_VIA_ID',[ ses(), robj['usr'] ], function(preq) {
+                            var r_au = preq.getResultObject();
+                            JSAN.use('patron.util');
+                            set(
+                                'prev_patron_name', 
+                                patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode(),
+                                robj['usr']
+                            );
+                            set_tooltip('prev_patron_name','circ chain prior to circ id ' + details.circ.id());
+                        });
+                    } else {
+                        set('prev_patron_name', document.getElementById(
+                            'circStrings').getString('staff.circ.aged_circ'));
                         set_tooltip('prev_patron_name','circ chain prior to circ id ' + details.circ.id());
-                    });
+                    }
                     set("prev_num_circs", summary.num_circs());
                     set("prev_num_renewals", Number(summary.num_circs()) - 1);
                     set("prev_xact_start", util.date.formatted_date( summary.start_time(), '%{localized}' )); 
diff --git a/Open-ILS/xul/staff_client/server/circ/checkin.js b/Open-ILS/xul/staff_client/server/circ/checkin.js
index 2cde9de..999ed31 100644
--- a/Open-ILS/xul/staff_client/server/circ/checkin.js
+++ b/Open-ILS/xul/staff_client/server/circ/checkin.js
@@ -190,7 +190,16 @@ circ.checkin.prototype = {
                             for (var i = 0; i < obj.selection_list.length; i++) {
                                 var circs = obj.network.simple_request('FM_CIRC_RETRIEVE_VIA_COPY',[ses(),obj.selection_list[i].copy_id,1]);
                                 if (circs.length > 0) {
-                                    patrons[circs[0].usr()] = 1;
+                                    if (circs[0].usr()) {
+                                        patrons[circs[0].usr()] = 1;
+                                    } else {
+                                        alert(
+                                            document.getElementById('circStrings')
+                                            .getFormattedString(
+                                                'staff.circ.item_no_user', 
+                                                [obj.selection_list[i].barcode])
+                                        );
+                                    }
                                 } else {
                                     alert(document.getElementById('circStrings').getFormattedString('staff.circ.item_no_circs', [obj.selection_list[i].barcode]));
                                 }
diff --git a/Open-ILS/xul/staff_client/server/circ/circ_brief.xul b/Open-ILS/xul/staff_client/server/circ/circ_brief.xul
index c207a3a..9b1eb06 100644
--- a/Open-ILS/xul/staff_client/server/circ/circ_brief.xul
+++ b/Open-ILS/xul/staff_client/server/circ/circ_brief.xul
@@ -68,13 +68,21 @@
                         if (get_bool(r_circ.opac_renewal() ) ) r += 'OPAC ';
                         if (get_bool(r_circ.phone_renewal() ) ) r += 'PHONE ';
                         $('renewal').value = r || document.getElementById('circStrings').getString('staff.circ.checkout.no.btn');
-                        g.patron_id = r_circ.usr(); $('add_billing').disabled = false;
-                        g.network.simple_request('FM_AU_FLESHED_RETRIEVE_VIA_ID',[ ses(), r_circ.usr() ], function(preq) {
-                            var r_au = preq.getResultObject();
-                            JSAN.use('patron.util'); 
-                            $('patron_name').value = patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode();
-                            patron.util.set_penalty_css(r_au);
-                        });
+                        g.patron_id = r_circ.usr(); 
+                        if (g.patron_id) {
+                            $('add_billing').disabled = false;
+                            g.network.simple_request(
+                                'FM_AU_FLESHED_RETRIEVE_VIA_ID',[ ses(), r_circ.usr() ], function(preq) {
+                                var r_au = preq.getResultObject();
+                                JSAN.use('patron.util'); 
+                                $('patron_name').value = patron.util.format_name( r_au ) + ' : ' + r_au.card().barcode();
+                                patron.util.set_penalty_css(r_au);
+                            });
+                        } else {
+                            $('patron_name').value = 
+                                document.getElementById('circStrings')
+                                .getString('staff.circ.aged_circ');
+                        }
 
                     } catch(E) {
                         g.error.standard_unexpected_error_alert(document.getElementById('circStrings').getString('staff.circ.circ_brief.failure'), E);
@@ -82,7 +90,7 @@
                 }
 
                 if (g.circ_id) {
-                    g.network.simple_request( 'FM_CIRC_RETRIEVE_VIA_ID', [ ses(), g.circ_id ], circ_callback);
+                    g.network.simple_request( 'FM_CIRC_RETRIEVE_VIA_ID', [ ses(), g.circ_id, true ], circ_callback);
                 } else {
                     g.circ = g.data.temp_circ; g.data.temp_circ = null; g.data.stash('temp_circ');
                     g.circ_id = g.data.temp_circ_id; g.data.temp_circ_id = null; g.data.stash('temp_circ_id');
@@ -90,7 +98,7 @@
                     if (g.circ) {
                         circ_callback( { 'getResultObject' : function() { return g.circ; } } );
                     } else {
-                        g.network.simple_request( 'FM_CIRC_RETRIEVE_VIA_ID', [ ses(), g.circ_id ], circ_callback);
+                        g.network.simple_request( 'FM_CIRC_RETRIEVE_VIA_ID', [ ses(), g.circ_id, true ], circ_callback);
                     }
                 }
 
diff --git a/Open-ILS/xul/staff_client/server/circ/renew.js b/Open-ILS/xul/staff_client/server/circ/renew.js
index cf538a1..5e5b017 100644
--- a/Open-ILS/xul/staff_client/server/circ/renew.js
+++ b/Open-ILS/xul/staff_client/server/circ/renew.js
@@ -152,7 +152,16 @@ circ.renew.prototype = {
                             for (var i = 0; i < obj.selection_list.length; i++) {
                                 var circs = obj.network.simple_request('FM_CIRC_RETRIEVE_VIA_COPY',[ses(),obj.selection_list[i].copy_id,1]);
                                 if (circs.length > 0) {
-                                    patrons[circs[0].usr()] = 1;
+                                    if (circs[0].usr()) {
+                                        patrons[circs[0].usr()] = 1;
+                                    } else {
+                                        alert(
+                                            document.getElementById('circStrings')
+                                            .getFormattedString(
+                                                'staff.circ.item_no_user', 
+                                                [obj.selection_list[i].barcode])
+                                        );
+                                    }
                                 } else {
                                     alert(document.getElementById('circStrings').getFormattedString('staff.circ.item_no_circs', [obj.selection_list[i].barcode]));
                                 }
diff --git a/Open-ILS/xul/staff_client/server/circ/util.js b/Open-ILS/xul/staff_client/server/circ/util.js
index 9d372d8..346a425 100644
--- a/Open-ILS/xul/staff_client/server/circ/util.js
+++ b/Open-ILS/xul/staff_client/server/circ/util.js
@@ -141,6 +141,10 @@ circ.util.show_last_few_circs = function(selection_list) {
             if (typeof my_xulG.retrieve_these_patrons == 'undefined') continue;
             var patrons = my_xulG.retrieve_these_patrons;
             for (var j = 0; j < patrons.length; j++) {
+
+                // combcirc objects may have a null value for user
+                if (!patrons[j]) continue;
+
                 if (typeof window.xulG == 'object' && typeof window.xulG.new_tab == 'function') {
                     try {
                         window.xulG.new_patron_tab( {}, { 'id' : patrons[j] } );
diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties b/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
index 0e6cd05..926e3a2 100644
--- a/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
+++ b/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
@@ -1,5 +1,7 @@
 staff.circ.alert=Alert
 staff.circ.item_no_circs=Item %1$s has never circulated.
+staff.circ.item_no_user=Item %1$s circulation is an aged circulation and has no linked user.
+staff.circ.aged_circ=<Aged Circulation>
 staff.circ.invalid_date=Invalid Date
 staff.circ.future_date=Future Date
 staff.circ.process_item=Check In / Process Item

commit 11d7f2206e4583db64626f9761c00c7746242cbe
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Aug 3 13:52:33 2016 -0400

    LP#1497335 Browser client item details aged circs
    
    1. Use all_circulation (combcirc) class (now accessible via pcrud) to
    render the item Circ History List tab.  In cases where a combcirc object
    has no 'usr' value, the interface displays <Aged Circulation> where
    the patron's name would normally be and disables the 'Add Billing'
    button.
    
    2. Handle null 'usr' values in the item status Recent Circ History tab.
    When a renewal chain summary has no 'usr' value, the interface displays
    <Aged Circulation> where the patron's name would normally be.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2
index 05ee925..698869b 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_circ_list_pane.tt2
@@ -17,17 +17,20 @@
 <div class="row" ng-repeat="circ in circ_list">
   <div class="flex-row">
     <div class="flex-cell well">
-      <a href="./circ/patron/{{circ.usr().id()}}/checkout" target="_self">
+      <a ng-if="circ.usr()" target="_self"
+        href="./circ/patron/{{circ.usr().id()}}/checkout">
         [% l('[_1], [_2] [_3] : [_4]', 
           '{{circ.usr().family_name()}}'
           '{{circ.usr().first_given_name()}}'
           '{{circ.usr().second_given_name()}}'
           '{{circ.usr().card().barcode()}}') %]
       </a>
+      <span ng-if="!circ.usr()">[% l('<Aged Circulation>') | html %]</span>
       <span class="pad-horiz">[% l('Circulation ID: [_1]', '{{circ.id()}}') %]</span>
     </div>
     <div>
-      <button class="btn btn-default" ng-click="addBilling(circ)">
+      <button class="btn btn-default" ng-click="addBilling(circ)" 
+        ng-disabled="!circ.usr()">
         [% l('Add Billing') %]
       </button>
     </div>
diff --git a/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2 b/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2
index 24a3241..512f5c3 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_circs_pane.tt2
@@ -14,13 +14,16 @@
     <div class="flex-cell">[% l('Patron') %]</div>
     <div class="flex-cell well">
       <a href="./circ/patron/{{prev_circ_usr.id()}}/checkout" 
-        ng-if="prev_circ_summary" target="_self">
+        ng-if="prev_circ_usr" target="_self">
         [% l('[_1], [_2] [_3] : [_4]', 
-          '{{prev_circ_usr.family_name()}}'
-          '{{prev_circ_usr.first_given_name()}}'
-          '{{prev_circ_usr.second_given_name()}}'
-          '{{prev_circ_usr.card().barcode()}}') %]
+        '{{prev_circ_usr.family_name()}}'
+        '{{prev_circ_usr.first_given_name()}}'
+        '{{prev_circ_usr.second_given_name()}}'
+        '{{prev_circ_usr.card().barcode()}}') %]
       </a>
+      <span ng-show="!prev_circ_usr">
+        [% l('<Aged Circulation>') | html %]
+    </span>
     </div>
   </div>
 
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index 922a531..7a26635 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -902,6 +902,7 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
         delete $scope.circ;
         delete $scope.circ_summary;
         delete $scope.prev_circ_summary;
+        delete $scope.prev_circ_usr;
         if (!copyId) return;
         
         egCore.pcrud.search('circ', 
@@ -945,12 +946,12 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
             ).then(null, null, function(summary) {
                 $scope.prev_circ_summary = summary.summary;
 
-                egCore.pcrud.retrieve('au', summary.usr,
-                    {flesh : 1, flesh_fields : {au : ['card']}})
+                if (summary.usr) { // aged circs have no 'usr'.
+                    egCore.pcrud.retrieve('au', summary.usr,
+                        {flesh : 1, flesh_fields : {au : ['card']}})
 
-                .then(function(user) {
-                    $scope.prev_circ_usr = user;
-                });
+                    .then(function(user) { $scope.prev_circ_usr = user });
+                }
             });
         });
     }
@@ -976,7 +977,8 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
     $scope.retrieveAllPatrons = function() {
         var users = new Set();
         angular.forEach($scope.circ_list.map(function(circ) { return circ.usr(); }),function(usr) {
-            users.add(usr);
+            // aged circs have no 'usr'.
+            if (usr) users.add(usr);
         });
         users.forEach(function(usr) {
             $timeout(function() {
@@ -1010,11 +1012,11 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
 
         }).then(function(count) {
 
-            egCore.pcrud.search('circ', 
+            egCore.pcrud.search('combcirc', 
                 {target_copy : copyId},
                 {   flesh : 2,
                     flesh_fields : {
-                        circ : [
+                        combcirc : [
                             'usr',
                             'workstation',                                         
                             'checkin_workstation',                                 
@@ -1022,7 +1024,7 @@ function($scope , $q , $location , $routeParams , $timeout , $window , egCore ,
                         ],
                         au : ['card']
                     },
-                    order_by : {circ : 'xact_start desc'}, 
+                    order_by : {combcirc : 'xact_start desc'}, 
                     limit :  count
                 }
 

commit 52bf3fe8e791c43448c49fa9818f5790625c724c
Author: Bill Erickson <berickxx at gmail.com>
Date:   Wed Aug 3 13:34:29 2016 -0400

    LP#1497335 Aged/All circulation API access
    
    Various SQL, IDL, and API changes for accessing aged circulations,
    primarily via all_circulation objects, for imporoved staff client
    integration.
    
    *. Support open-ils.pcrud access to the action.all_circulation DB view /
       'combcirc' class.
    
    *. Add missing parent_circ, checkin_scan_time, checkin_workstation to
       combcirc class.
    
    *. Add 'usr' field to combcirc.  The action.all_circulation VIEW will
       return NULL as the 'usr' column value when returning data for an
       aged_circulation.
    
    *. Add virtual 'active_circ' and 'aged_circ' fields to combcirc for
       fleshing the related action.circulation or action.aged_circulation
       object.
    
    * Adds SQL functions for action.all_circ_chain and
      action.summarize_all_circ_chain, which pull data from
      aged_circulation.
    
    *. API calls
    open-ils.circ.[prev_]renewal_chain.retrieve_by_circ[.summary] now return
    data for active and aged circulations by using action.all_circ_chain and
    action.summarize_all_circ_chain.  When using these APIs, a null value
    in the 'usr' column is the indication that a given circulation or circ
    chain summary represents an aged circulation.
    
    * API open-ils.circ.copy_details.retrieve will now optionally return
      aged circ data within the copy circ history.
    
    Signed-off-by: Bill Erickson <berickxx at gmail.com>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 485e4c6..7126f16 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -4209,7 +4209,7 @@ SELECT  usr,
 			</actions>
 		</permacrud>
 	</class>
-	<class id="combcirc" controller="open-ils.cstore" oils_obj:fieldmapper="action::all_circulation" oils_persist:tablename="action.all_circulation" reporter:core="true" reporter:label="Combined Aged and Active Circulations" oils_persist:readonly="true">
+	<class id="combcirc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::all_circulation" oils_persist:tablename="action.all_circulation" reporter:core="true" reporter:label="Combined Aged and Active Circulations" oils_persist:readonly="true">
 		<fields oils_persist:primary="id" oils_persist:sequence="money.billable_xact_id_seq">
 			<field reporter:label="Check In Library" name="checkin_lib" reporter:datatype="org_unit"/>
 			<field reporter:label="Check In Staff" name="checkin_staff" reporter:datatype="link"/>
@@ -4237,6 +4237,10 @@ SELECT  usr,
 			<field reporter:label="Transaction Finish Date/Time" name="xact_finish" reporter:datatype="timestamp" />
 			<field reporter:label="Checkout Date/Time" name="xact_start" reporter:datatype="timestamp" />
 			<field reporter:label="Record Creation Date/Time" name="create_time" reporter:datatype="timestamp" />
+			<field reporter:label="Parent Circulation" name="parent_circ" reporter:datatype="link"/>
+			<field reporter:label="Checkin Scan Time" name="checkin_scan_time" reporter:datatype="timestamp"/>
+			<field reporter:label="Checkin Workstation" name="checkin_workstation" reporter:datatype="link"/>
+			<field reporter:label="Patron" name="usr" reporter:datatype="link"/>
 			<field reporter:label="Transaction Billings" name="billings" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Transaction Payments" name="payments" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Base Transaction" name="billable_transaction" oils_persist:virtual="true" reporter:datatype="link"/>
@@ -4254,6 +4258,8 @@ SELECT  usr,
 			<field reporter:label="Bib Record" name="copy_bib_record" reporter:datatype="link"/>
 			<field reporter:label="Archived Patron Stat-Cat Entries" name="aaactsc_entries" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Archived Copy Stat-Cat Entries" name="aaasc_entries" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Linked Active Circulation" name="active_circ" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="Linked Aged Circulation" name="aged_circ" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="billable_transaction" reltype="might_have" key="id" map="" class="mbt"/>
@@ -4275,12 +4281,22 @@ SELECT  usr,
 			<link field="copy_owning_lib" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="copy_circ_lib" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="workstation" reltype="has_a" key="id" map="" class="aws"/>
+			<link field="checkin_workstation" reltype="has_a" key="id" map="" class="aws"/>
 			<link field="copy_bib_record" reltype="has_a" key="id" map="" class="bre"/>
 			<link field="aaactsc_entries" reltype="has_many" key="xact" map="" class="aaactsc"/>
 			<link field="aaasc_entries" reltype="has_many" key="xact" map="" class="aaasc"/>
 			<link field="usr_home_ou" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="usr_profile" reltype="has_a" key="id" map="" class="pgt"/>
+			<link field="active_circ" reltype="might_have" key="id" map="" class="circ"/>
+			<link field="aged_circ" reltype="might_have" key="id" map="" class="acirc"/>
+			<link field="parent_circ" reltype="might_have" key="id" map="" class="acirc"/>
+			<link field="usr" reltype="has_a" key="id" map="" class="au"/>
 		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<retrieve permission="VIEW_CIRCULATIONS" context_field="circ_lib" />
+			</actions>
+		</permacrud>
 	</class>
 	<class id="acirc" controller="open-ils.cstore" oils_obj:fieldmapper="action::aged_circulation" oils_persist:tablename="action.aged_circulation" reporter:core="true" reporter:label="Aged (patronless) Circulation">
 		<fields oils_persist:primary="id" oils_persist:sequence="money.billable_xact_id_seq">
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
index 0fad7d9..83b9789 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
@@ -1811,7 +1811,7 @@ sub create_uuid_string {
 
 sub create_circ_chain_summary {
     my($class, $e, $circ_id) = @_;
-    my $sum = $e->json_query({from => ['action.summarize_circ_chain', $circ_id]})->[0];
+    my $sum = $e->json_query({from => ['action.summarize_all_circ_chain', $circ_id]})->[0];
     return undef unless $sum;
     my $obj = Fieldmapper::action::circ_chain_summary->new;
     $obj->$_($sum->{$_}) for keys %$sum;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index ba1133d..530da64 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -55,14 +55,20 @@ __PACKAGE__->register_method(
         Retrieve a circ object by id
         @param authtoken Login session key
         @pararm circid The id of the circ object
+        @param all_circ Returns an action.all_circulation object instead
+            of an action.circulation object to pick up aged circs.
     /
 );
+
 sub retrieve_circ {
-    my( $s, $c, $a, $i ) = @_;
+    my( $s, $c, $a, $i, $all_circ ) = @_;
     my $e = new_editor(authtoken => $a);
     return $e->event unless $e->checkauth;
-    my $circ = $e->retrieve_action_circulation($i) or return $e->event;
-    if( $e->requestor->id ne $circ->usr ) {
+    my $method = $all_circ ?
+        'retrieve_action_all_circulation' :
+        'retrieve_action_circulation';
+    my $circ = $e->$method($i) or return $e->event;
+    if( $e->requestor->id ne ($circ->usr || '') ) {
         return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
     }
     return $circ;
@@ -783,7 +789,7 @@ sub view_circs {
         $count = 4 unless defined $count;
     }
 
-    return $e->search_action_circulation([
+    return $e->search_action_all_circulation([
         {target_copy => $copyid}, 
         {limit => $count, order_by => { circ => "xact_start DESC" }} 
     ]);
@@ -1074,35 +1080,33 @@ sub copy_details {
     my $transit = $e->search_action_transit_copy(
         { target_copy => $copy_id, dest_recv_time => undef } )->[0];
 
-    # find the latest circ, open or closed
-    my $circ = $e->search_action_circulation(
-        [
-            { target_copy => $copy_id },
-            { 
-                flesh => 1,
-                flesh_fields => {
-                    circ => [
-                        'workstation',
-                        'checkin_workstation', 
-                        'duration_rule', 
-                        'max_fine_rule', 
-                        'recurring_fine_rule'
-                    ]
-                },
-                order_by => { circ => 'xact_start desc' }, 
-                limit => 1 
-            }
-        ]
-    )->[0];
-
+    # find the most recent circulation for the requested copy,
+    # be it active, completed, or aged.
+    my $circ = $e->search_action_all_circulation([
+        { target_copy => $copy_id },
+        {
+            flesh => 1,
+            flesh_fields => {
+                combcirc => [
+                    'workstation',
+                    'checkin_workstation',
+                    'duration_rule',
+                    'max_fine_rule',
+                    'recurring_fine_rule'
+                ],
+            },
+            order_by => { combcirc => 'xact_start desc' },
+            limit => 1
+        }
+    ])->[0];
 
     return {
-        copy        => $copy,
-        hold        => $hold,
+        copy    => $copy,
+        hold    => $hold,
         transit => $transit,
-        circ        => $circ,
+        circ    => $circ,
         volume  => $vol,
-        mvr     => $mvr,
+        mvr     => $mvr
     };
 }
 
@@ -1796,10 +1800,10 @@ sub retrieve_circ_chain {
 
     } else {
 
-        my $chain = $e->json_query({from => ['action.circ_chain', $circ_id]});
+        my $chain = $e->json_query({from => ['action.all_circ_chain', $circ_id]});
 
         for my $circ_info (@$chain) {
-            my $circ = Fieldmapper::action::circulation->new;
+            my $circ = Fieldmapper::action::all_circulation->new;
             $circ->$_($circ_info->{$_}) for keys %$circ_info;
             $conn->respond($circ);
         }
@@ -1844,51 +1848,51 @@ sub retrieve_prev_circ_chain {
     return $e->event unless $e->checkauth;
     return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
 
-    if($self->api_name =~ /summary/) {
-        my $first_circ = $e->json_query({from => ['action.circ_chain', $circ_id]})->[0];
-        my $target_copy = $$first_circ{'target_copy'};
-        my $usr = $$first_circ{'usr'};
-        my $last_circ_from_prev_chain = $e->json_query({
-            'select' => { 'circ' => ['id','usr'] },
-            'from' => 'circ', 
-            'where' => {
-                target_copy => $target_copy,
-                xact_start => { '<' => $$first_circ{'xact_start'} }
+    my $first_circ = 
+        $e->json_query({from => ['action.all_circ_chain', $circ_id]})->[0];
+
+    my $prev_circ = $e->search_action_all_circulation([
+        {   target_copy => $first_circ->{target_copy},
+            xact_start => {'<' => $first_circ->{xact_start}}
+        }, {   
+            flesh => 1,
+            flesh_fields => {
+                combcirc => [
+                    'active_circ',
+                    'aged_circ'
+                ]
             },
-            'order_by' => [{ 'class'=>'circ', 'field'=>'xact_start', 'direction'=>'desc' }],
-            'limit' => 1
-        })->[0];
-        return undef unless $last_circ_from_prev_chain;
-        return undef unless $$last_circ_from_prev_chain{'id'};
-        my $sum = $e->json_query({from => ['action.summarize_circ_chain', $$last_circ_from_prev_chain{'id'}]})->[0];
-        return undef unless $sum;
-        my $obj = Fieldmapper::action::circ_chain_summary->new;
-        $obj->$_($sum->{$_}) for keys %$sum;
-        return { 'summary' => $obj, 'usr' => $$last_circ_from_prev_chain{'usr'} };
+            order_by => { combcirc => 'xact_start desc' },
+            limit => 1 
+        }
+    ])->[0];
 
-    } else {
+    return undef unless $prev_circ;
 
-        my $first_circ = $e->json_query({from => ['action.circ_chain', $circ_id]})->[0];
-        my $target_copy = $$first_circ{'target_copy'};
-        my $last_circ_from_prev_chain = $e->json_query({
-            'select' => { 'circ' => ['id'] },
-            'from' => 'circ', 
-            'where' => {
-                target_copy => $target_copy,
-                xact_start => { '<' => $$first_circ{'xact_start'} }
-            },
-            'order_by' => [{ 'class'=>'circ', 'field'=>'xact_start', 'direction'=>'desc' }],
-            'limit' => 1
+    my $chain_usr = $prev_circ->usr; # note: may be undef
+
+    if ($self->api_name =~ /summary/) {
+        my $sum = $e->json_query({
+            from => [
+                'action.summarize_all_circ_chain', 
+                $prev_circ->id
+            ]
         })->[0];
-        return undef unless $last_circ_from_prev_chain;
-        return undef unless $$last_circ_from_prev_chain{'id'};
-        my $chain = $e->json_query({from => ['action.circ_chain', $$last_circ_from_prev_chain{'id'}]});
 
-        for my $circ_info (@$chain) {
-            my $circ = Fieldmapper::action::circulation->new;
-            $circ->$_($circ_info->{$_}) for keys %$circ_info;
-            $conn->respond($circ);
-        }
+        my $summary = Fieldmapper::action::circ_chain_summary->new;
+        $summary->$_($sum->{$_}) for keys %$sum;
+
+        return {summary => $summary, usr => $chain_usr};
+    }
+
+
+    my $chain = $e->json_query(
+        {from => ['action.all_circ_chain', $prev_circ->id]});
+
+    for my $circ_info (@$chain) {
+        my $circ = Fieldmapper::action::all_circulation->new;
+        $circ->$_($circ_info->{$_}) for keys %$circ_info;
+        $conn->respond($circ);
     }
 
     return undef;
diff --git a/Open-ILS/src/sql/Pg/090.schema.action.sql b/Open-ILS/src/sql/Pg/090.schema.action.sql
index 5ce80d3..731ab3c 100644
--- a/Open-ILS/src/sql/Pg/090.schema.action.sql
+++ b/Open-ILS/src/sql/Pg/090.schema.action.sql
@@ -239,7 +239,8 @@ CREATE OR REPLACE VIEW action.all_circulation AS
         circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, grace_period, due_date,
         stop_fines_time, checkin_time, create_time, duration, fine_interval, recurring_fine,
         max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recurring_fine_rule,
-        max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
+        max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ,
+        NULL AS usr
       FROM  action.aged_circulation
             UNION ALL
     SELECT  DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
@@ -248,7 +249,7 @@ CREATE OR REPLACE VIEW action.all_circulation AS
         circ.checkin_lib, circ.renewal_remaining, circ.grace_period, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
         circ.fine_interval, circ.recurring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
         circ.recurring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
-        circ.parent_circ
+        circ.parent_circ, circ.usr
       FROM  action.circulation circ
         JOIN asset.copy cp ON (circ.target_copy = cp.id)
         JOIN asset.call_number cn ON (cp.call_number = cn.id)
@@ -883,6 +884,95 @@ BEGIN
 END;
 $$ LANGUAGE 'plpgsql';
 
+-- same as action.circ_chain, but returns action.all_circulation 
+-- rows which may include aged circulations.
+CREATE OR REPLACE FUNCTION action.all_circ_chain (ctx_circ_id INTEGER) 
+    RETURNS SETOF action.all_circulation AS $$
+DECLARE
+    tmp_circ action.all_circulation%ROWTYPE;
+    circ_0 action.all_circulation%ROWTYPE;
+BEGIN
+
+    SELECT INTO tmp_circ * FROM action.all_circulation WHERE id = ctx_circ_id;
+
+    IF tmp_circ IS NULL THEN
+        RETURN NEXT tmp_circ;
+    END IF;
+    circ_0 := tmp_circ;
+
+    -- find the front of the chain
+    WHILE TRUE LOOP
+        SELECT INTO tmp_circ * FROM action.all_circulation 
+            WHERE id = tmp_circ.parent_circ;
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        circ_0 := tmp_circ;
+    END LOOP;
+
+    -- now send the circs to the caller, oldest to newest
+    tmp_circ := circ_0;
+    WHILE TRUE LOOP
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        RETURN NEXT tmp_circ;
+        SELECT INTO tmp_circ * FROM action.all_circulation 
+            WHERE parent_circ = tmp_circ.id;
+    END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+-- same as action.summarize_circ_chain, but returns data collected
+-- from action.all_circulation, which may include aged circulations.
+CREATE OR REPLACE FUNCTION action.summarize_all_circ_chain 
+    (ctx_circ_id INTEGER) RETURNS action.circ_chain_summary AS $$
+
+DECLARE
+
+    -- first circ in the chain
+    circ_0 action.all_circulation%ROWTYPE;
+
+    -- last circ in the chain
+    circ_n action.all_circulation%ROWTYPE;
+
+    -- circ chain under construction
+    chain action.circ_chain_summary;
+    tmp_circ action.all_circulation%ROWTYPE;
+
+BEGIN
+    
+    chain.num_circs := 0;
+    FOR tmp_circ IN SELECT * FROM action.all_circ_chain(ctx_circ_id) LOOP
+
+        IF chain.num_circs = 0 THEN
+            circ_0 := tmp_circ;
+        END IF;
+
+        chain.num_circs := chain.num_circs + 1;
+        circ_n := tmp_circ;
+    END LOOP;
+
+    chain.start_time := circ_0.xact_start;
+    chain.last_stop_fines := circ_n.stop_fines;
+    chain.last_stop_fines_time := circ_n.stop_fines_time;
+    chain.last_checkin_time := circ_n.checkin_time;
+    chain.last_checkin_scan_time := circ_n.checkin_scan_time;
+    SELECT INTO chain.checkout_workstation name FROM actor.workstation WHERE id = circ_0.workstation;
+    SELECT INTO chain.last_checkin_workstation name FROM actor.workstation WHERE id = circ_n.checkin_workstation;
+
+    IF chain.num_circs > 1 THEN
+        chain.last_renewal_time := circ_n.xact_start;
+        SELECT INTO chain.last_renewal_workstation name FROM actor.workstation WHERE id = circ_n.workstation;
+    END IF;
+
+    RETURN chain;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
 CREATE OR REPLACE FUNCTION action.usr_visible_holds (usr_id INT) RETURNS SETOF action.hold_request AS $func$
 DECLARE
     h               action.hold_request%ROWTYPE;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql
new file mode 100644
index 0000000..b618980
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.aged-circ-chains.sql
@@ -0,0 +1,141 @@
+
+BEGIN;
+
+-- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+DROP VIEW IF EXISTS action.all_circulation;
+CREATE VIEW action.all_circulation AS
+     SELECT aged_circulation.id, aged_circulation.usr_post_code,
+        aged_circulation.usr_home_ou, aged_circulation.usr_profile,
+        aged_circulation.usr_birth_year, aged_circulation.copy_call_number,
+        aged_circulation.copy_location, aged_circulation.copy_owning_lib,
+        aged_circulation.copy_circ_lib, aged_circulation.copy_bib_record,
+        aged_circulation.xact_start, aged_circulation.xact_finish,
+        aged_circulation.target_copy, aged_circulation.circ_lib,
+        aged_circulation.circ_staff, aged_circulation.checkin_staff,
+        aged_circulation.checkin_lib, aged_circulation.renewal_remaining,
+        aged_circulation.grace_period, aged_circulation.due_date,
+        aged_circulation.stop_fines_time, aged_circulation.checkin_time,
+        aged_circulation.create_time, aged_circulation.duration,
+        aged_circulation.fine_interval, aged_circulation.recurring_fine,
+        aged_circulation.max_fine, aged_circulation.phone_renewal,
+        aged_circulation.desk_renewal, aged_circulation.opac_renewal,
+        aged_circulation.duration_rule,
+        aged_circulation.recurring_fine_rule,
+        aged_circulation.max_fine_rule, aged_circulation.stop_fines,
+        aged_circulation.workstation, aged_circulation.checkin_workstation,
+        aged_circulation.checkin_scan_time, aged_circulation.parent_circ,
+        NULL AS usr
+       FROM action.aged_circulation
+UNION ALL
+     SELECT DISTINCT circ.id,
+        COALESCE(a.post_code, b.post_code) AS usr_post_code,
+        p.home_ou AS usr_home_ou, p.profile AS usr_profile,
+        date_part('year'::text, p.dob)::integer AS usr_birth_year,
+        cp.call_number AS copy_call_number, circ.copy_location,
+        cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
+        cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish,
+        circ.target_copy, circ.circ_lib, circ.circ_staff,
+        circ.checkin_staff, circ.checkin_lib, circ.renewal_remaining,
+        circ.grace_period, circ.due_date, circ.stop_fines_time,
+        circ.checkin_time, circ.create_time, circ.duration,
+        circ.fine_interval, circ.recurring_fine, circ.max_fine,
+        circ.phone_renewal, circ.desk_renewal, circ.opac_renewal,
+        circ.duration_rule, circ.recurring_fine_rule, circ.max_fine_rule,
+        circ.stop_fines, circ.workstation, circ.checkin_workstation,
+        circ.checkin_scan_time, circ.parent_circ, circ.usr
+       FROM action.circulation circ
+  JOIN asset.copy cp ON circ.target_copy = cp.id
+JOIN asset.call_number cn ON cp.call_number = cn.id
+JOIN actor.usr p ON circ.usr = p.id
+LEFT JOIN actor.usr_address a ON p.mailing_address = a.id
+LEFT JOIN actor.usr_address b ON p.billing_address = b.id;
+
+
+CREATE OR REPLACE FUNCTION action.all_circ_chain (ctx_circ_id INTEGER) 
+    RETURNS SETOF action.all_circulation AS $$
+DECLARE
+    tmp_circ action.all_circulation%ROWTYPE;
+    circ_0 action.all_circulation%ROWTYPE;
+BEGIN
+
+    SELECT INTO tmp_circ * FROM action.all_circulation WHERE id = ctx_circ_id;
+
+    IF tmp_circ IS NULL THEN
+        RETURN NEXT tmp_circ;
+    END IF;
+    circ_0 := tmp_circ;
+
+    -- find the front of the chain
+    WHILE TRUE LOOP
+        SELECT INTO tmp_circ * FROM action.all_circulation 
+            WHERE id = tmp_circ.parent_circ;
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        circ_0 := tmp_circ;
+    END LOOP;
+
+    -- now send the circs to the caller, oldest to newest
+    tmp_circ := circ_0;
+    WHILE TRUE LOOP
+        IF tmp_circ IS NULL THEN
+            EXIT;
+        END IF;
+        RETURN NEXT tmp_circ;
+        SELECT INTO tmp_circ * FROM action.all_circulation 
+            WHERE parent_circ = tmp_circ.id;
+    END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+CREATE OR REPLACE FUNCTION action.summarize_all_circ_chain 
+    (ctx_circ_id INTEGER) RETURNS action.circ_chain_summary AS $$
+
+DECLARE
+
+    -- first circ in the chain
+    circ_0 action.all_circulation%ROWTYPE;
+
+    -- last circ in the chain
+    circ_n action.all_circulation%ROWTYPE;
+
+    -- circ chain under construction
+    chain action.circ_chain_summary;
+    tmp_circ action.all_circulation%ROWTYPE;
+
+BEGIN
+    
+    chain.num_circs := 0;
+    FOR tmp_circ IN SELECT * FROM action.all_circ_chain(ctx_circ_id) LOOP
+
+        IF chain.num_circs = 0 THEN
+            circ_0 := tmp_circ;
+        END IF;
+
+        chain.num_circs := chain.num_circs + 1;
+        circ_n := tmp_circ;
+    END LOOP;
+
+    chain.start_time := circ_0.xact_start;
+    chain.last_stop_fines := circ_n.stop_fines;
+    chain.last_stop_fines_time := circ_n.stop_fines_time;
+    chain.last_checkin_time := circ_n.checkin_time;
+    chain.last_checkin_scan_time := circ_n.checkin_scan_time;
+    SELECT INTO chain.checkout_workstation name FROM actor.workstation WHERE id = circ_0.workstation;
+    SELECT INTO chain.last_checkin_workstation name FROM actor.workstation WHERE id = circ_n.checkin_workstation;
+
+    IF chain.num_circs > 1 THEN
+        chain.last_renewal_time := circ_n.xact_start;
+        SELECT INTO chain.last_renewal_workstation name FROM actor.workstation WHERE id = circ_n.workstation;
+    END IF;
+
+    RETURN chain;
+
+END;
+$$ LANGUAGE 'plpgsql';
+
+
+COMMIT;
+

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

Summary of changes:
 Open-ILS/examples/fm_IDL.xml                       |   18 +++-
 .../perlmods/lib/OpenILS/Application/AppUtils.pm   |    2 +-
 .../src/perlmods/lib/OpenILS/Application/Circ.pm   |  148 ++++++++++----------
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/090.schema.action.sql          |   94 ++++++++++++-
 .../Pg/upgrade/0998.schema.aged-circ-chains.sql    |  141 +++++++++++++++++++
 .../templates/staff/cat/item/t_circ_list_pane.tt2  |    7 +-
 .../src/templates/staff/cat/item/t_circs_pane.tt2  |   13 +-
 Open-ILS/web/js/ui/default/staff/cat/item/app.js   |   20 ++--
 Open-ILS/xul/staff_client/server/cat/util.js       |   14 ++
 .../server/circ/alternate_copy_summary.js          |   53 +++++---
 Open-ILS/xul/staff_client/server/circ/checkin.js   |   11 ++-
 .../xul/staff_client/server/circ/circ_brief.xul    |   26 +++--
 .../xul/staff_client/server/circ/circ_summary.xul  |   14 ++
 Open-ILS/xul/staff_client/server/circ/renew.js     |   11 ++-
 Open-ILS/xul/staff_client/server/circ/util.js      |    4 +
 .../server/locale/en-US/circ.properties            |    2 +
 .../Circulation/aged-circs-see-light-of-day.adoc   |   21 +++
 18 files changed, 478 insertions(+), 123 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0998.schema.aged-circ-chains.sql
 create mode 100644 docs/RELEASE_NOTES_NEXT/Circulation/aged-circs-see-light-of-day.adoc


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list