select refunds; bills OUS; grid refactoring
authorBill Erickson <berick@esilibrary.com>
Mon, 2 Jun 2014 15:06:06 +0000 (11:06 -0400)
committerBill Erickson <berick@esilibrary.com>
Mon, 2 Jun 2014 15:06:06 +0000 (11:06 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/cat/item/t_list.tt2
Open-ILS/src/templates/staff/circ/patron/t_bill_history_payments.tt2
Open-ILS/src/templates/staff/circ/patron/t_bill_history_xacts.tt2
Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2
Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2
Open-ILS/src/templates/staff/share/t_autogrid.tt2
Open-ILS/web/js/ui/default/staff/cat/item/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/bills.js
Open-ILS/web/js/ui/default/staff/services/grid.js

index e472107..8c296a8 100644 (file)
@@ -4,7 +4,7 @@
   features="-display,-sort,-multisort"
   main-label="[% l('Item Status') %]"
   items-provider="gridDataProvider"
-  on-item-retrieved="gridItemRetrieved"
+  grid-controls="gridControls"
   persist-key="cat.items">
 
   <eg-grid-field label="[% l('Barcode') %]"     path='barcode' visible></eg-grid-field>
index cba0df1..823ef72 100644 (file)
@@ -6,9 +6,7 @@
     id-field="id"
     query="paymentQuery"
     sort="paymentSort"
-    activate-item="activatePayment"
-    selected-items="getSelectedItems"
-    all-items="getAllItems"
+    grid-controls="gridControls"
     revision="paymentRevision">
 
     <eg-grid-action 
index d3b87b0..e72b8dd 100644 (file)
@@ -5,9 +5,7 @@
     idl-class="mbt"
     id-field="id"
     query="xactQuery"
-    activate-item="activateBill"
-    selected-items="getSelectedItems"
-    all-items="getAllItems"
+    grid-controls="gridControls"
     revision="xactRevision">
 
     <eg-grid-action 
index 1a4bdf7..0e31b75 100644 (file)
@@ -3,11 +3,7 @@
   idl-class="mbt"
   query="gridQuery"
   sort="gridSort"
-  activate-item="activateBill"
-
-  on-item-retrieved="gridItemRetrieved"
-  selected-items="getSelectedItems"
-  all-items="getAllItems"
+  grid-controls="gridControls"
   revision="gridRevision"
   persist-key="circ.patron.bills">
 
index 7d74f43..ac83e6d 100644 (file)
@@ -2,9 +2,8 @@
   idl-class="au" id-field="id"
   features="-sort,-display,-multisort"
   main-label="[% l('Patron Search Results') %]"
+  grid-controls="gridControls"
   items-provider="patronSearchGridProvider"
-  activate-item="activatePatron"
-  selected-items="getSelectedItems"
   persist-key="circ.patron.search">
   <eg-grid-field label="[% ('ID') %]" path='id' visible></eg-grid-field>
   <eg-grid-field label="[% ('Card') %]" path='card.barcode' visible></eg-grid-field>
index 27f01c8..8c8c6ff 100644 (file)
     <div class="eg-grid-cell eg-grid-cell-stock">
       <div>
         <input title="[% l('Row Selector Column') %]"
+          focus-me="gridControls.focusRowSelector"
           type='checkbox' ng-model="selectAll"/> 
       </div>
     </div>
         ng-class="{'eg-grid-row-selected' : selected[indexValue(item)]}">
       <div class="eg-grid-cell eg-grid-cell-stock"
         ng-click="handleRowClick($event, item)" title="[% l('Row Index') %]">
-        <a href ng-show="activateItem" ng-click="activateItem(item)" style="font-weight:bold">
+        <a href ng-show="gridControls.activateItem" 
+          ng-click="gridControls.activateItem(item)" style="font-weight:bold">
           {{$index + offset() + 1}}
         </a>
-        <div ng-hide="activateItem">{{$index + offset() + 1}}</div>
+        <div ng-hide="gridControls.activateItem">{{$index + offset() + 1}}</div>
       </div>
       <div class="eg-grid-cell eg-grid-cell-stock">
         <!-- ng-click=handleRowClick here has unintended 
index b6b8cda..980714a 100644 (file)
@@ -177,11 +177,13 @@ function($scope , $q , $location , $timeout , egCore , egGridDataProvider , item
     // If a copy was just displayed in the detail view, ensure it's
     // focused in the list view.
     var selected = false;
-    $scope.gridItemRetrieved = function(item) {
-        if (selected || !itemSvc.copy) return;
-        if (itemSvc.copy.id() == item.id) {
-            provider.selectOneItem(item.index);
-            selected = true;
+    $scope.gridControls = {
+        itemRetrieved : function(item) {
+            if (selected || !itemSvc.copy) return;
+            if (itemSvc.copy.id() == item.id) {
+                provider.selectOneItem(item.index);
+                selected = true;
+            }
         }
     };
 
index 8bbd044..d6a7e91 100644 (file)
@@ -19,8 +19,11 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap',
         // fetch the org settings we care about during egStartup
         // and toss them into egCore.env as egCore.env.aous[name] = value.
         egCore.env.classLoaders.aous = function() {
-            return egCore.org.settings(['circ.obscure_dob'])
-            .then(function(settings) { egCore.env.aous = settings });
+            return egCore.org.settings(
+            [
+                'circ.obscure_dob',
+                'ui.circ.show_billing_tab_on_bills',
+            ]).then(function(settings) { egCore.env.aous = settings });
         }
 
         egCore.env.classLoaders.pgt = function() {
@@ -338,6 +341,18 @@ function($scope,  $q,  $location , $filter,  egCore,  egUser,  patronSvc) {
             $location
                 .path('/circ/patron/' + patronSvc.current.id() + '/alerts')
                 .search('card', null);
+            return;
+        }
+
+        // no alert required.  If the patron has fines and the show-bills
+        // OUS is applied, direct to the bills page.
+        if ($scope.patron_stats().fines.balance_owed > 0 // TODO: != 0 ?
+            && egCore.env.aous['ui.circ.show_billing_tab_on_bills']
+            && !$location.path().match(/bills$/)) {
+
+            $location
+                .path('/circ/patron/' + patronSvc.current.id() + '/bills')
+                .search('card', null);
         }
     }
 
@@ -466,8 +481,12 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
     $scope.initTab('search');
     $scope.focusMe = true;
     $scope.searchArgs = {};
-    $scope.activatePatron = function(usr) {
-        $location.path('/circ/patron/' + usr.id() + '/checkout');
+
+    $scope.gridControls = {
+        activateItem : function(item) {
+            $location.path('/circ/patron/' + item.id() + '/checkout');
+        },
+        selectedItems : function() {return []}
     }
 
     // cache of object field values; speeds up grid rendering for
@@ -486,9 +505,8 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
         return {offset : 0};
     }
 
-    $scope.getSelectedItems = function() {return []};
     $scope.$watch(
-        function() {return $scope.getSelectedItems()},
+        function() {return $scope.gridControls.selectedItems()},
         function(list) {
             if (list[0]) 
                 patronSvc.setDefault(null, list[0]);
index 7bdbd58..17ef6df 100644 (file)
@@ -9,6 +9,14 @@ function($q , egCore , patronSvc) {
 
     var service = {};
 
+    // fetch org unit settings specific to the bills display
+    service.fetchBillSettings = function() {
+        if (service.settings) return $q.when(service.settings);
+        return egCore.org.settings(
+            ['ui.circ.billing.uncheck_bills_and_unfocus_payment_box']
+        ).then(function(s) {return service.settings = s});
+    }
+
     // user billing summary
     service.fetchSummary = function() {
         return egCore.pcrud.retrieve(
@@ -214,9 +222,21 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     $scope.focus_payment = true;
     $scope.annotate_payment = false;
     $scope.gridRevision = 0;
-    var allItems = []; // all grid items (flattened mbt's)
-    // until the grid wakes up, return empty set
-    $scope.getSelectedItems = $scope.getAllItems = function() {return []};
+
+    // pre-define list-returning funcs in case we access them
+    // before the grid instantiates
+    $scope.gridControls = {
+        focusRowSelector : false,
+        selectItems : function(){},
+        selectedItems : function(){return []},
+        allItems : function(){return []},
+        itemRetrieved : function(item) {
+            item.payment_pending = 0;
+        },
+        activateItem : function(item) {
+            $scope.showFullDetails([item]);
+        }
+    }
 
     billSvc.fetchSummary().then(function(s) {$scope.summary = s});
 
@@ -237,7 +257,7 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     // TODO: move me to service
     function selected_payment_info() {
         var info = {owed : 0, billed : 0, paid : 0};
-        angular.forEach($scope.getSelectedItems(), function(item) {
+        angular.forEach($scope.gridControls.selectedItems(), function(item) {
             info.owed   += Number(item['summary.balance_owed']) * 100;
             info.billed += Number(item['summary.total_owed']) * 100;
             info.paid   += Number(item['summary.total_paid']) * 100;
@@ -265,7 +285,7 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     }
     $scope.refunds_available = function() {
         var amount = 0;
-        angular.forEach($scope.getAllItems(), function(item) {
+        angular.forEach($scope.gridControls.allItems(), function(item) {
             if (item['summary.balance_owed'] < 0) 
                 amount += item['summary.balance_owed'] * 100;
         });
@@ -281,14 +301,11 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     $scope.gridQuery = function() {return query};
     $scope.gridSort = ['xact_start']; // default sort
 
-    $scope.gridItemRetrieved = function(item) {
-        item.payment_pending = 0;
-    }
 
     // update the item.payment_pending value each time the user
     // selects different transactions to pay against.
     $scope.$watch(
-        function() {return $scope.getSelectedItems()},
+        function() {return $scope.gridControls.selectedItems()},
         function() {updatePendingColumn()},
         true
     );
@@ -303,12 +320,12 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     // of our local scope variables.
     function updatePendingColumn() {
         // reset all to zero..
-        angular.forEach($scope.getAllItems(), 
+        angular.forEach($scope.gridControls.allItems(), 
             function(item) {item.payment_pending = 0});
 
         var payment_amount = $scope.pending_payment();
 
-        var selected = $scope.getSelectedItems();
+        var selected = $scope.gridControls.selectedItems();
         for (var i = 0; i < selected.length; i++) { // for/break
             var item = selected[i];
             var owed = Number(item['summary.balance_owed']);
@@ -333,7 +350,7 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     // which have a pending payment amount.
     function generatePayments() {
         var payments = [];
-        angular.forEach($scope.getSelectedItems(), function(item) {
+        angular.forEach($scope.gridControls.selectedItems(), function(item) {
             if (item.payment_pending == 0) return;
             payments.push([item.id, item.payment_pending]);
         });
@@ -404,14 +421,51 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
         );
     }
 
+    // Select refunds adds all refunds to the existing selection.
+    // It does not /only/ select refunds
     $scope.selectRefunds = function() {
-        // TODO: select grid items where refunds are due
+        var ids = $scope.gridControls.selectedItems().map(
+            function(i) { return i.id });
+        angular.forEach($scope.gridControls.allItems(), function(item) {
+            if (Number(item['summary.balance_owed']) < 0)
+                ids.push(item.id);
+        });
+        $scope.gridControls.selectItems(ids);
+    }
+
+    // -------------
+    // determine on initial page load when all of the grid rows should
+    // be selected.
+    var selectOnLoad = true;
+    billSvc.fetchBillSettings().then(function(s) {
+        if (s['ui.circ.billing.uncheck_bills_and_unfocus_payment_box']) {
+            $scope.focus_payment = false; // de-focus the payment box
+            $scope.gridControls.focusRowSelector = true;
+            selectOnLoad = false;
+            // if somehow the grid finishes rendering before our settings 
+            // arrive, manually de-select everything.
+            $scope.gridControls.selectItems([]);
+        }
+    });
+    $scope.gridControls.allItemsRetrieved = function() {
+        if (selectOnLoad) {
+            // select all non-refund items
+            $scope.gridControls.selectItems( 
+                $scope.gridControls.allItems()
+                .filter(function(i) {return i['summary.balance_owed'] > 0})
+                .map(function(i){return i.id})
+            );
+        }
     }
+    // -------------
+
 
     $scope.printBills = function(selected) {
+        if (!selected.length) return;
         // bills print receipt assumes nested hashes, but our grid
         // stores flattener data.  Fetch the selected xacts as
-        // fleshed pcrud objects.  (Consider an alternate approach..)
+        // fleshed pcrud objects and hashify.  
+        // (Consider an alternate approach..)
         var ids = selected.map(function(t){ return t.id });
         var xacts = [];
         egCore.pcrud.search('mbt', 
@@ -669,13 +723,18 @@ function($scope,  $q , $routeParams , egCore , patronSvc , billSvc , egPromptDia
 .controller('BillXactHistoryCtrl',
        ['$scope','$q','egCore','patronSvc','billSvc','egPromptDialog','$location',
 function($scope,  $q , egCore , patronSvc , billSvc , egPromptDialog , $location) {
-    $scope.getSelectedItems = function() {return []};
 
+    $scope.gridControls = {
+        selectedItems : function(){return []},
+        activateItem : function(item) {
+            $scope.showFullDetails([item]);
+        }
+    }
 
     // TODO; move me to service
     function selected_payment_info() {
         var info = {owed : 0, billed : 0, paid : 0};
-        angular.forEach($scope.getSelectedItems(), function(item) {
+        angular.forEach($scope.gridControls.selectedItems(), function(item) {
             info.owed   += Number(item['summary.balance_owed']) * 100;
             info.billed += Number(item['summary.total_owed']) * 100;
             info.paid   += Number(item['summary.total_paid']) * 100;
@@ -710,16 +769,17 @@ function($scope,  $q , egCore , patronSvc , billSvc , egPromptDialog , $location
             $location.path('/circ/patron/' + 
                 patronSvc.current.id() + '/bill/' + all[0].id);
     }
-    $scope.activateBill = function(xact) {
-        $scope.showFullDetails([xact]);
-    }
-
 }])
 
 .controller('BillPaymentHistoryCtrl',
        ['$scope','$q','egCore','patronSvc','billSvc','$location',
 function($scope,  $q , egCore , patronSvc , billSvc , $location) {
-    $scope.getSelectedItems = function() {return []};
+    $scope.gridControls = {
+        selectedItems : function(){return []},
+        activateItem : function(item) {
+            $scope.showFullDetails([item]);
+        }
+    }
 
     $scope.paymentSort = [{'payment_ts' : 'DESC'}, 'id'];
     $scope.paymentQuery = function() {
@@ -735,13 +795,9 @@ function($scope,  $q , egCore , patronSvc , billSvc , $location) {
                 patronSvc.current.id() + '/bill/' + all[0]['xact.id']);
     }
 
-    $scope.activatePayment = function(payment) {
-        $scope.showFullDetails([payment]);
-    }
-
     $scope.totals.selected_paid = function() {
         var paid = 0;
-        angular.forEach($scope.getSelectedItems(), function(payment) {
+        angular.forEach($scope.gridControls.selectedItems(), function(payment) {
             paid += Number(payment.amount) * 100;
         });
         return paid / 100;
index 446b635..57af67e 100644 (file)
@@ -68,35 +68,24 @@ angular.module('egGridMod',
             // optional context menu label
             menuLabel : '@',
 
-            // called on each item retrieved in collect() with the item
-            // as the argument.  Useful for modiying objects before they
-            // are absorbed by the grid.
-            onItemRetrieved : '=',
-
-            // TODO: deprecated
-            // function called with all selected items each time the
-            // selection set changes
-            //onSelectionChange : '=',
-
-            // function;  if set, row index values will be hyperlinked and
-            // the onclick for an item will call activateItem with the item
-            // as the argument.
-            activateItem : '=',
-
-            // Function.  Returns an array of selected items to the caller.
-            // The contents of this function are defined by the grid and
-            // called by the caller.
-            selectedItems : '=',
-
-            // Function.  Returns an array of all grid items.
-            // The contents of this function are defined by the grid and
-            // called by the caller.
-            allItems : '=',
-
-            // Function.  Accepts an array of index values.
-            // The contents of this function are defined by the grid and
-            // called by the caller.
-            //setSelected : '=',
+            // Hash of control functions.
+            //
+            //  These functions are defined by the calling scope and 
+            //  invoked as-is by the grid w/ the specified parameters.
+            //
+            //  itemRetrieved     : function(item) {}
+            //  allItemsRetrieved : function() {}
+            //
+            //  ---------------
+            //  These functions are defined by the grid and thus
+            //  replace any values defined for these attributes from the
+            //  calling scope.
+            //
+            //  activateItem  : function(item) {}
+            //  allItems      : function(allItems) {}
+            //  selectedItems : function(selected) {}
+            //  selectItems   : function(ids) {}
+            gridControls : '=',
 
             // if set, we watch this scope variable for changes.  If it
             // changes, we refresh the grid.
@@ -148,9 +137,11 @@ angular.module('egGridMod',
                     }
                 }
 
+                grid.gridControls = $scope.gridControls || {};
+
                 // items needed only by the grid; remove from scope
                 angular.forEach( 
-                    ['idlClass','onItemRetrieved','onSelectionChange','persistKey'], 
+                    ['idlClass', 'persistKey'], 
                     function(field) {
                         grid[field] = $scope[field];
                         delete $scope[field];
@@ -264,24 +255,18 @@ angular.module('egGridMod',
 
                 // only link the caller mediation functions into 
                 // the scope if the caller defines handlers. 
-                if ($scope.selectedItems) {
-                    console.log('updating selecte..');
-                    $scope.selectedItems = function() {
-                        return grid.getSelectedItems()
-                    };
+                grid.gridControls.selectedItems = function() {
+                    return grid.getSelectedItems()
                 }
-                if ($scope.allItems) {
-                    $scope.allItems = function() {
-                        return grid.items;
-                    }
+                grid.gridControls.allItems = function() {
+                    return $scope.items;
                 }
-                if ($scope.setSelected) {
-                    $scope.setSelected = function(ids) {
-                        $scope.selected = {};
-                        angular.forEach(ids, function(id) {
-                            $scope.selected[''+id] = true;
-                        });
-                    }
+                grid.gridControls.selectItems = function(ids) {
+                    if (!ids) return;
+                    $scope.selected = {};
+                    angular.forEach(ids, function(i) {
+                        $scope.selected[''+i] = true;
+                    });
                 }
 
                 $scope.itemFieldValue = grid.dataProvider.itemFieldValue;
@@ -813,12 +798,17 @@ angular.module('egGridMod',
                 $scope.items = [];
                 $scope.selected = {};
                 grid.collecting = true;
-                grid.dataProvider.get(grid.offset, grid.limit)
-                .then(null, null, function(item) {
+                grid.dataProvider.get(grid.offset, grid.limit).then(
+                function() {
+                    if (grid.gridControls.allItemsRetrieved)
+                        grid.gridControls.allItemsRetrieved();
+                },
+                null, 
+                function(item) {
                     if (item) {
                         $scope.items.push(item)
-                        if (grid.onItemRetrieved)
-                            grid.onItemRetrieved(item);
+                        if (grid.gridControls.itemRetrieved)
+                            grid.gridControls.itemRetrieved(item);
                     }
                 })['finally'](function() { grid.collecting = false })
             }
@@ -1121,11 +1111,19 @@ angular.module('egGridMod',
             
             if (!column.label) {
                 column.label = idl_info.idl_field.label || column.name;
+                /*
+                // append class label to column label to better differentiate
+                // columsn in the selector.
+                // Disabled for now, since it results in columns w/ really
+                // long names, making the grid unappealing when any of
+                // these colmns are selected.
+                // TODO: consider nesting the colum picker by class?
                 if (fromExpand) {
                     var label = 
                         idl_info.idl_class.label || idl_info.idl_class.name;
                     column.label = label + '::' + column.label;
                 }
+                */
             }
         },