xact details page cont.
authorBill Erickson <berick@esilibrary.com>
Wed, 28 May 2014 14:48:05 +0000 (10:48 -0400)
committerBill Erickson <berick@esilibrary.com>
Wed, 28 May 2014 14:48:05 +0000 (10:48 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/circ/patron/index.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/circ/patron/t_xact_details.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/bills.js
Open-ILS/web/js/ui/default/staff/services/grid.js
Open-ILS/web/js/ui/default/staff/services/ui.js

index 05b3ef3..ed33ead 100644 (file)
@@ -29,7 +29,8 @@
 angular.module('egCoreMod').run(['egStrings', function(s) {
   s.ANNOTATE_PAYMENT_MSG = "[% l('Please annotate this payment') %]";
   s.CONFIRM_REFUND_PAYMENT = 
-    "[% |l('{{xactIds}}') -%]Are you sure you would like to refund excess payment on bills [_1]?  This action will simply put the amount in the Payment Pending column as a negative value.  You must still select Apply Payment!  Certain types of payments may not be refunded.  The refund may be applied to checked transactions that follow the refunded transaction.[% END %]"
+    "[% |l('{{xactIds}}') -%]Are you sure you would like to refund excess payment on bills [_1]?  This action will simply put the amount in the Payment Pending column as a negative value.  You must still select Apply Payment!  Certain types of payments may not be refunded.  The refund may be applied to checked transactions that follow the refunded transaction.[% END %]";
+  s.EDIT_BILL_PAY_NOTE = "[% l('Enter new note for #[_1]:','{{ids}}') %]";
 }]);
 </script>
 
index b88ff6e..241869e 100644 (file)
@@ -3,6 +3,7 @@
   idl-class="mobts"
   query="gridQuery"
   sort="gridSort"
+  activate-item="activateBill"
 
   on-item-retrieved="gridItemRetrieved"
   on-selection-change="gridSelectionChanged"
index 38e715c..16ba3da 100644 (file)
@@ -1,5 +1,5 @@
 <eg-grid
-  idl-class="au" sort="[]" id-field="id"
+  idl-class="au" id-field="id"
   features="-sort,-display,-multisort"
   main-label="[% l('Patron Search Results') %]"
   items-provider="patronSearchGridProvider"
index 79d4d50..11a8c7c 100644 (file)
@@ -1,14 +1,10 @@
-<h2>[% l('Transaction Details') %]</h2>
+<h2>[% l('Details for Transaction #[_1]', '{{xact.id()}}') %]</h2>
 
 <h3>[% l('Summary') %]</h3>
 
 <div class="row">
   <div class="col-md-2 strong-text">[% l('Billing Location') %]</div>
   <div class="col-md-2">{{xact.billing_location().shortname()}}</div>
-</div>
-<div class="row">
-  <div class="col-md-2 strong-text">[% l('Bill #') %]</div>
-  <div class="col-md-2">{{xact.id()}}</div>
   <div class="col-md-2 strong-text">[% l('Total Billed') %]</div>
   <div class="col-md-2">{{xact.total_owed() | currency}}</div>
   <div class="col-md-2 strong-text">[% l('Title') %]</div>
   <div class="col-md-2 strong-text">[% l('Checked In') %]</div>
   <div class="col-md-2">{{xact.circulation().checkin_time() | date:'short'}}</div>
 </div>
+<div class="row">
+  <div class="col-md-2 strong-text">[% l('Barcode') %]</div>
+  <div class="col-md-2">
+    <a title="[% l('Item Details') %]" target="_self"
+      href='./cat/item/{{xact.circulation().target_copy().id()}}'>
+      {{xact.circulation().target_copy().barcode()}}
+    </a>
+  </div>
+  <div class="col-md-2 strong-text">[% l('Call Number') %]</div>
+  <div class="col-md-2">
+    {{xact.circulation().target_copy().call_number().label()}}
+  </div>
+  <div class="col-md-2 strong-text">[% l('Location') %]</div>
+  <div class="col-md-2">
+    {{xact.circulation().target_copy().location().name()}}
+  </div>
+</div>
+
 
 <!-- TODO item summary -->
 
   auto-fields="true"
   page-size="10"
   items-provider="billGridProvider">
+
+  <eg-grid-action 
+    label="[% l('Void Billings') %]" handler="voidBillings"></eg-grid-action>
+
+  <eg-grid-action 
+    label="[% l('Edit Note') %]" handler="editBillNotes"></eg-grid-action>
+
 </eg-grid>
 
 <!-- TODO: this grid may contain objects of different types.. apply manual columns, see xul -->
@@ -65,5 +86,7 @@
   auto-fields="true"
   page-size="10"
   items-provider="paymentGridProvider">
+  <eg-grid-action 
+    label="[% l('Edit Note') %]" handler="editPaymentNotes"></eg-grid-action>
 </eg-grid>
 
index c5fe127..d509a44 100644 (file)
@@ -111,6 +111,74 @@ function($q , egCore , patronSvc) {
         });
     }
 
+    service.fetchXact = function(id) {
+        return egCore.pcrud.retrieve('mobts', id, 
+            {   flesh : 5,
+                flesh_fields : {
+                    mobts : ['circulation','grocery'],
+                    circ : ['target_copy'],
+                    acp : ['call_number','location'],
+                    acn : ['record'],
+                    bre : ['simple_record']
+                },
+                select : { bre : ['id'] } // avoid MARC
+            },
+            {authoritative : true}
+        ).then(function(xact) {
+            xact.billing_location(egCore.org.get(xact.billing_location()));
+            return xact;
+        });
+    }
+
+    service.fetchBills = function(xact_id) {
+        return egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.money.billing.retrieve.all.authoritative',
+            egCore.auth.token(), xact_id
+        );
+    }
+
+    service.fetchPayments = function(xact_id) {
+        return egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.money.payment.retrieve.all.authoritative',
+            egCore.auth.token(), xact_id
+        );
+    }
+
+    service.voidBills = function(bill_ids) {
+        return egCore.net.requestWithParamList(
+            'open-ils.circ',
+            'open-ils.circ.money.billing.void',
+            [egCore.auth.token()].concat(bill_ids)
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) return alert(evt);
+            return resp;
+        });
+    }
+
+    service.updateBillNotes = function(note, ids) {
+        return egCore.net.requestWithParamList(
+            'open-ils.circ',
+            'open-ils.circ.money.billing.note.edit',
+            [egCore.auth.token(), note].concat(ids)
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) return alert(evt);
+            return resp;
+        });
+    }
+
+    service.updatePaymentNotes = function(note, ids) {
+        return egCore.net.requestWithParamList(
+            'open-ils.circ',
+            'open-ils.circ.money.payment.note.edit',
+            [egCore.auth.token(), note].concat(ids)
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) return alert(evt);
+            return resp;
+        });
+    }
+
     return service;
 }])
 
@@ -325,7 +393,7 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
     $scope.applyPayment = function() {
         if ($scope.annotate_payment) {
             egPromptDialog.open(
-                egCore.strings.ANNOTATE_PAYMENT_MSG, 
+                egCore.strings.ANNOTATE_PAYMENT_MSG, '',
                 {ok : function(value) {sendPayment(value)}}
             );
         } else {
@@ -335,11 +403,8 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
 
     $scope.voidAllBillings = function(items) {
         angular.forEach(items, function(item) {
-            egCore.net.request(
-                'open-ils.circ',
-                'open-ils.circ.money.billing.retrieve.all.authoritative',
-                egCore.auth.token(), item.id
-            ).then(function(bills) {
+
+            billSvc.fetchBills(item.id).then(function(bills) {
                 var bill_ids = [];
                 var cents = 0;
                 angular.forEach(bills, function(b) {
@@ -357,12 +422,7 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
 
                 // TODO: alert of pending voiding
 
-                egCore.net.requestWithParamList(
-                    'open-ils.circ',
-                    'open-ils.circ.money.billing.void',
-                    [egCore.auth.token()].concat(bill_ids)
-                ).then(function(resp) {
-                    if (evt = egCore.evt.parse(resp)) return alert(evt);
+                billSvc.voidBills(bill_ids).then(function() {
                     refreshDisplay();
                 });
             });
@@ -396,40 +456,25 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
             $location.path('/circ/patron/' + 
                 patronSvc.current.id() + '/bill/' + all[0].id);
     }
+
+    $scope.activateBill = function(xact) {
+        $scope.showFullDetails([xact]);
+    }
+
 }])
 
 /**
  * Displays details of a single transaction
  */
 .controller('XactDetailsCtrl',
-       ['$scope','$q','$routeParams','egCore','egGridDataProvider',
-function($scope,  $q , $routeParams , egCore , egGridDataProvider) {
+       ['$scope','$q','$routeParams','egCore','egGridDataProvider','patronSvc','billSvc','egPromptDialog',
+function($scope,  $q , $routeParams , egCore , egGridDataProvider , patronSvc , billSvc , egPromptDialog) {
 
     $scope.initTab('bills', $routeParams.id);
     var xact_id = $routeParams.xact_id;
     var bills = [];
     var payments = [];
 
-    egCore.pcrud.retrieve('mobts', xact_id, 
-        {   flesh : 5,
-            flesh_fields : {
-                mobts : ['circulation','grocery'],
-                circ : ['target_copy'],
-                acp : ['call_number'],
-                acn : ['record'],
-                bre : ['simple_record']
-            },
-            // avoid fetching the MARC blob by specifying which 
-            // fields on the bre to select.  More may be needed.
-            // note that fleshed fields are explicitly selected.
-            select : { bre : ['id'] }
-        },
-        {authoritative : true}
-    ).then(function(xact) {
-        xact.billing_location(egCore.org.get(xact.billing_location()));
-        $scope.xact = xact;
-    });
-
     // --- bills grid
     var billProvider = egGridDataProvider.instance();
     billProvider.get = function(offset, count) {
@@ -438,15 +483,6 @@ function($scope,  $q , $routeParams , egCore , egGridDataProvider) {
     billProvider.itemFieldValue = billProvider.nestedItemFieldValue;
     $scope.billGridProvider = billProvider;
 
-    egCore.net.request(
-        'open-ils.circ',
-        'open-ils.circ.money.billing.retrieve.all.authoritative',
-        egCore.auth.token(), xact_id
-    ).then(function(mbs) {  
-        bills = mbs;
-        billProvider.increment();
-    });
-
     // --- payments grid
     var paymentProvider = egGridDataProvider.instance();
     paymentProvider.get = function(offset, count) {
@@ -455,15 +491,91 @@ function($scope,  $q , $routeParams , egCore , egGridDataProvider) {
     paymentProvider.itemFieldValue = paymentProvider.nestedItemFieldValue;
     $scope.paymentGridProvider = paymentProvider;
 
-    egCore.net.request(
-        'open-ils.circ',
-        'open-ils.circ.money.payment.retrieve.all.authoritative',
-        egCore.auth.token(), xact_id
-    ).then(function(mps) {
-        payments = mps;
-        paymentProvider.increment();
+    // -- actions
+    $scope.voidBillings = function(bill_list) {
+        var bill_ids = [];
+        angular.forEach(bill_list, function(b) {
+            if (b.voided() != 't') bill_ids.push(b.id());
+        });
+
+        if (bill_ids.length == 0) {
+            // TODO: warn
+            return;
+        }
+
+        billSvc.voidBills(bill_ids).then(function() {
+
+            // refresh bills and summary data
+            // note: no need to update payments
+            patronSvc.fetchUserStats();
+
+            billSvc.fetchXact(xact_id).then(function(xact) {
+                $scope.xact = xact
+            });
+        
+            billSvc.fetchBills(xact_id).then(function(bs) {
+                bills = bs; 
+                billProvider.increment();
+            });
+        });
+    }
+
+    // batch-edit billing and payment notes, depending on 'type'
+    function editNotes(selected, type) {
+        var notes = selected.map(function(b){ return b.note() }).join(',');
+        var ids = selected.map(function(b){ return b.id() });
+
+        // show the note edit prompt
+        egPromptDialog.open(
+            egCore.strings.EDIT_BILL_PAY_NOTE, notes, {
+                ids : ''+ids,
+                ok : function(value) {
+
+                    var func = 'updateBillNotes';
+                    if (type == 'payment') func = 'updatePaymentNotes';
+
+                    billSvc[func](value, ids).then(function() {
+
+                        if (type == 'payment') {
+                            billSvc.fetchPayments(xact_id).then(function(ps) {
+                                payments = ps;
+                                paymentProvider.increment();
+                            });
+
+                        } else {
+                            billSvc.fetchBills(xact_id).then(function(bs) {
+                                bills = bs; 
+                                billProvider.increment();
+                            });
+                        }
+                    });
+                }
+            }
+        );
+    }
+
+    $scope.editBillNotes = function(selected) {
+        editNotes(selected, 'bill');
+    }
+
+    $scope.editPaymentNotes = function(selected) {
+        editNotes(selected, 'payment');
+    }
+
+    // -- retrieve our data
+    billSvc.fetchXact(xact_id).then(function(xact) {
+        $scope.xact = xact;
     });
 
+    billSvc.fetchBills(xact_id).then(function(bs) {
+        bills = bs; 
+        billProvider.increment();
+    });
+
+    billSvc.fetchPayments(xact_id).then(function(ps) {
+        payments = ps;
+        paymentProvider.increment();
+    });
 }])
 
 
index cf80393..6be5c1b 100644 (file)
@@ -99,9 +99,9 @@ angular.module('egGridMod',
 
         controller : [
                     '$scope','$q','egCore','egGridFlatDataProvider','$location',
-                    'egGridColumnsProvider','$filter','$window','$sce',
+                    'egGridColumnsProvider','$filter','$window','$sce','$timeout',
             function($scope,  $q , egCore,  egGridFlatDataProvider , $location,
-                     egGridColumnsProvider , $filter , $window , $sce) {
+                     egGridColumnsProvider , $filter , $window , $sce , $timeout) {
 
             var grid = this;
 
@@ -438,10 +438,22 @@ angular.module('egGridMod',
                 return ''+item; 
             }
 
-            // fires the action handler function
+            // fires the action handler function for a context action
             $scope.actionLauncher = function(action) {
-                action.handler(grid.getSelectedItems());
                 $scope.gridActionsIsOpen = false;
+
+                if (!action.handler) {
+                    console.error(
+                        'No handler specified for "' + action.label + '"');
+                    return;
+                }
+
+                try {
+                    action.handler(grid.getSelectedItems());
+                } catch(E) {
+                    console.error('Error executing handler for "' 
+                        + action.label + '" => ' + E);
+                }
             }
 
             // returns the list of selected item objects
index ac090b1..6aec308 100644 (file)
@@ -141,10 +141,15 @@ function($modal, $interpolate) {
 }])
 
 /**
- * egPromptDialog.open("some message goes {{here}}", {
- *    here : 'foo',  
- *    ok : function(value) {console.log(value)}, 
- *    cancel : function() {}});
+ * egPromptDialog.open(
+ *    "prompt message goes {{here}}", 
+ *    promptValue,  // optional
+ *    {
+ *      here : 'foo',  
+ *      ok : function(value) {console.log(value)}, 
+ *      cancel : function() {console.log('prompt denied')}
+ *    }
+ *  );
  */
 .factory('egPromptDialog', 
     
@@ -152,13 +157,13 @@ function($modal, $interpolate) {
 function($modal, $interpolate) {
     var service = {};
 
-    service.open = function(message, msg_scope) {
+    service.open = function(message, promptValue, msg_scope) {
         return $modal.open({
             templateUrl: './share/t_prompt_dialog',
             controller: ['$scope', '$modalInstance',
                 function($scope, $modalInstance) {
                     $scope.message = $interpolate(message)(msg_scope);
-                    $scope.args = {value : ''};
+                    $scope.args = {value : promptValue || ''};
                     $scope.focus = true;
                     $scope.ok = function() {
                         if (msg_scope.ok) msg_scope.ok($scope.args.value);