bill history transactions
authorBill Erickson <berick@esilibrary.com>
Wed, 28 May 2014 20:11:59 +0000 (16:11 -0400)
committerBill Erickson <berick@esilibrary.com>
Wed, 28 May 2014 20:11:59 +0000 (16:11 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/patron/t_xact_details.tt2
Open-ILS/src/templates/staff/css/style.css.tt2
Open-ILS/src/templates/staff/share/t_autogrid.tt2
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/pcrud.js
Open-ILS/web/js/ui/default/staff/services/ui.js

index 43558dd..eceafd0 100644 (file)
@@ -1968,7 +1968,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
             </actions>
         </permacrud>
        </class>
-       <class id="mbts" controller="open-ils.cstore" oils_obj:fieldmapper="money::billable_transaction_summary" oils_persist:tablename="money.materialized_billable_xact_summary" reporter:label="Billable Transaction Summary" oils_persist:readonly="true">
+       <class id="mbts" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="money::billable_transaction_summary" oils_persist:tablename="money.materialized_billable_xact_summary" reporter:label="Billable Transaction Summary" oils_persist:readonly="true">
                <fields oils_persist:primary="id" oils_persist:sequence="">
                        <field reporter:label="Balance Owed" name="balance_owed" reporter:datatype="money"/>
                        <field reporter:label="Transaction ID" name="id" reporter:datatype="id"/>
@@ -1988,6 +1988,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                <links>
                        <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_USER_TRANSACTIONS">
+                                       <context link="usr" field="home_ou" />
+                               </retrieve>
+                       </actions>
+               </permacrud>
        </class>
        <class id="mbtslv" controller="open-ils.cstore" oils_obj:fieldmapper="money::billable_transaction_summary_location_view" oils_persist:tablename="money.billable_xact_summary_location_view" reporter:label="Billable Transaction Summary with Billing Location" oils_persist:readonly="true" reporter:core="true">
                <fields oils_persist:primary="id" oils_persist:sequence="">
@@ -6139,7 +6146,7 @@ SELECT  usr,
             </actions>
         </permacrud>
        </class>
-       <class id="mbt" controller="open-ils.cstore" oils_obj:fieldmapper="money::billable_transaction" oils_persist:tablename="money.billable_xact" reporter:label="Billable Transaction">
+       <class id="mbt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="money::billable_transaction" oils_persist:tablename="money.billable_xact" reporter:label="Billable Transaction">
                <fields oils_persist:primary="id" oils_persist:sequence="money.billable_xact_id_seq">
                        <field reporter:label="Transaction ID" name="id" reporter:datatype="id" />
                        <field reporter:label="User" name="usr" reporter:datatype="link"/>
@@ -6164,6 +6171,13 @@ SELECT  usr,
                        <link field="payment_total" reltype="might_have" key="xact" map="" class="rxpt"/>
                        <link field="summary" reltype="might_have" key="id" map="" class="mbts"/>
                </links>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <retrieve permission="VIEW_USER_TRANSACTIONS">
+                                       <context link="usr" field="home_ou" />
+                               </retrieve>
+                       </actions>
+               </permacrud>
        </class>
        <class id="actsce" controller="open-ils.cstore" oils_obj:fieldmapper="actor::stat_cat_entry" oils_persist:tablename="actor.stat_cat_entry" reporter:label="User Stat Cat Entry">
                <fields oils_persist:primary="id" oils_persist:sequence="actor.stat_cat_entry_id_seq">
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bill_history.tt2
new file mode 100644 (file)
index 0000000..1f2c75a
--- /dev/null
@@ -0,0 +1,84 @@
+<h2>[% l('Bill History') %]</h2>
+
+<ul class="nav nav-tabs">
+  <li ng-class="{active : bill_tab == 'transactions'}">
+    <a href="./circ/patron/{{patron().id()}}/bill_history/transactions">
+        [% l('Transactions') %]
+    </a>
+  </li>
+  <li ng-class="{active : bill_tab == 'payments'}">
+    <a href="./circ/patron/{{patron().id()}}/bill_history/payments">
+        [% l('Payments') %]
+    </a>
+  </li>
+</ul>
+<div class="tab-content">
+  <div class="tab-pane active">
+
+    <div ng-if="bill_tab == 'transactions'">
+      <div class="flex-row padded">
+        <div>[% l('Selected Billed:') %]</div>
+        <div>{{selected_billed | currency}}</div>
+        <div>[% l('Selected Paid:') %]</div>
+        <div>{{selected_paid | currency}}</div>
+        <div class="flex-cell"></div>
+        <div>[% l('Start Date:') %]</div>
+        <div><input eg-date-input class="form-control" ng-model="dates.xact_start"/></div>
+        <div>[% l('End Date:') %]</div>
+        <div><input eg-date-input class="form-control" ng-model="dates.xact_finish"/></div>
+      </div><!-- top row -->
+      <hr/>
+
+      <eg-grid
+        idl-class="mbt"
+        id-field="id"
+        query="xactQuery"
+        activate-item="activateBill"
+        revision="xactRevision">
+
+        <eg-grid-action 
+          label="[% l('Add Billing') %]" handler=""></eg-grid-action>
+        <eg-grid-action 
+          label="[% l('Full Details') %]" handler="showFullDetails"></eg-grid-action>
+
+        <eg-grid-field path="summary.balance_owed"></eg-grid-field>
+        <eg-grid-field path="id" label="[% l('Bill #') %]"></eg-grid-field>
+        <eg-grid-field path="xact_finish" label="[% l('Finish') %]"></eg-grid-field>
+        <eg-grid-field path="xact_start" label="[% l('Start') %]"></eg-grid-field>
+        <eg-grid-field path="summary.total_owed" label="[% l('Total Billed') %]"></eg-grid-field>
+        <eg-grid-field path="summary.total_paid" label="[% l('Total Paid') %]"></eg-grid-field>
+        <eg-grid-field path="summary.xact_type" label="[% l('Type') %]"></eg-grid-field>
+
+
+        <eg-grid-field label="[% l('Title') %]" name="title" 
+          path="circulation.target_copy.call_number.record.simple_record.title">
+          <a href="[% ctx.base_path %]/opac/record/{{item.record_id}}">{{item.title}}</a>
+        </eg-grid-field>
+
+        <!-- needed for bib link -->
+        <eg-grid-field name="record_id" 
+          path="circulation.target_copy.call_number.record.id" 
+          required hidden></eg-grid-field>
+
+        <eg-grid-field label="[% l('Barcode') %]" name="copy_barcode" 
+          path="circulation.target_copy.barcode">
+          <a target="_self" href="./cat/item/{{item.copy_id}}">{{item.copy_barcode}}</a>
+        </eg-grid-field>
+
+        <!-- needed for item link -->
+        <eg-grid-field name="copy_id" 
+          path="circulation.target_copy.id" required hidden></eg-grid-field>
+
+        <!-- needed for grid query -->
+        <eg-grid-field path="summary.last_payment_ts" required hidden></eg-grid-field>
+
+        <eg-grid-field path="summary.*" hidden></eg-grid-field>
+        <eg-grid-field path="circulation.target_copy.*" hidden></eg-grid-field>
+        <eg-grid-field path="circulation.target_copy.call_number.*" hidden></eg-grid-field>
+
+      </eg-grid>
+    </div><!-- xacts -->
+
+  </div>
+</div>
+
index a676a38..0e5807e 100644 (file)
@@ -4,7 +4,7 @@
   <div class="col-md-2 strong-text">[% l('Billing Location') %]</div>
   <div class="col-md-2">{{xact.billing_location().shortname()}}</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">{{xact.summary().total_owed() | currency}}</div>
   <div class="col-md-2 strong-text">[% l('Title') %]</div>
   <div class="col-md-2">
     <a ng-if="title_id" href="[% ctx.base_path %]/opac/record/{{title_id}}">{{title}}</a>
@@ -13,9 +13,9 @@
 </div>
 <div class="row">
   <div class="col-md-2 strong-text">[% l('Type') %]</div>
-  <div class="col-md-2">{{xact.xact_type()}}</div>
+  <div class="col-md-2">{{xact.summary().xact_type()}}</div>
   <div class="col-md-2 strong-text">[% l('Total Paid') %]</div>
-  <div class="col-md-2">{{xact.total_paid() | currency}}</div>
+  <div class="col-md-2">{{xact.summary().total_paid() | currency}}</div>
   <div class="col-md-2 strong-text">[% l('Checked Out') %]</div>
   <div class="col-md-2">{{xact.circulation().xact_start() | date:'short'}}</div>
 </div>
@@ -23,7 +23,7 @@
   <div class="col-md-2 strong-text">[% l('Start') %]</div>
   <div class="col-md-2">{{xact.xact_start() | date:'short'}}</div>
   <div class="col-md-2 strong-text">[% l('Total Billed') %]</div>
-  <div class="col-md-2">{{xact.balance_owed() | currency}}</div>
+  <div class="col-md-2">{{xact.summary().balance_owed() | currency}}</div>
   <div class="col-md-2 strong-text">[% l('Due Date') %]</div>
   <div class="col-md-2">{{xact.circulation().due_date() | date:'short'}}</div>
 </div>
index 8b51943..c30215b 100644 (file)
@@ -351,6 +351,9 @@ table.list tr.selected td {
 .flex-row {
   display: flex;
 }
+.flex-row.padded div {
+  padding: 5px;
+}
 .flex-cell {
   flex: 1;
   padding: 4px; /* bootstrap default is much bigger */
index 883d3dd..27f01c8 100644 (file)
         <div style="display:flex">
           <div style="flex:1" class="eg-grid-column-move-handle">
             <div ng-if="col.sortable">
-              <a column="{{col.name}}" href='' 
+              <a column="{{col.name}}" href
                 eg-grid-column-drag-source
                 ng-click="quickSort(col.name)">{{col.label}}</a>
             </div>
index 462d22d..5d59b3e 100644 (file)
@@ -103,6 +103,12 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap',
         resolve : resolver
     });
 
+    $routeProvider.when('/circ/patron/:id/bill_history/:history_tab', {
+        templateUrl: './circ/patron/t_bill_history',
+        controller: 'BillHistoryCtrl',
+        resolve : resolver
+    });
+
     $routeProvider.when('/circ/patron/:id/messages', {
         templateUrl: './circ/patron/t_messages',
         controller: 'PatronMessagesCtrl',
index 6e23b44..a360c6c 100644 (file)
@@ -111,21 +111,33 @@ function($q , egCore , patronSvc) {
         });
     }
 
+    service.xactFlesh = {   
+        flesh : 5,
+        flesh_fields : {
+            mbt : ['summary','circulation','grocery','reservation'],
+            circ : ['target_copy'],
+            acp : ['call_number','location','status','age_protect'],
+            acn : ['record'],
+            bre : ['simple_record']
+        },
+        select : { bre : ['id'] } // avoid MARC
+    }
+
     service.fetchXact = function(id) {
-        return egCore.pcrud.retrieve('mobts', id, 
-            {   flesh : 5,
-                flesh_fields : {
-                    mobts : ['circulation','grocery'],
-                    circ : ['target_copy'],
-                    acp : ['call_number','location','status','age_protect'],
-                    acn : ['record'],
-                    bre : ['simple_record']
-                },
-                select : { bre : ['id'] } // avoid MARC
-            },
-            {authoritative : true}
+        return egCore.pcrud.retrieve(
+            'mbt', id, service.xactFlesh, {authoritative : true}
         ).then(function(xact) {
-            xact.billing_location(egCore.org.get(xact.billing_location()));
+
+            /* mobts has location, but mbts does not
+             * only the xact detail page shows it now, so maybe move
+             * this into the template...
+             *
+            var loc;
+            if (xact.circulation()) loc = xact.circulation().circ_lib();
+            if (xact.reservation()) loc = xact.reservation().pickup_lib();
+            if (xact.grocery()) loc = xact.grocery().billing_location();
+            xact.billing_location(egCore.org.get(loc));
+            */
             return xact;
         });
     }
@@ -335,6 +347,11 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
             billSvc.fetchSummary().then(function(s) {$scope.summary = s});
         })
     }
+
+    $scope.showHistory = function() {
+        $location.path('/circ/patron/' + 
+            patronSvc.current.id() + '/bill_history/transactions');
+    }
     
     // For now, only adds billing to first selected item.
     // Could do batches later if needed
@@ -378,16 +395,12 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
         );
     }
 
-    $scope.showHistory = function() {
-        // go to bills/history
-    }
-
     $scope.selectRefunds = function() {
-        // select grid items where refunds are due
+        // TODO: select grid items where refunds are due
     }
 
     $scope.printBills = function() {
-        // print selected bills using the bills print template
+        // TODO: print selected bills using the bills print template
     }
 
     $scope.applyPayment = function() {
@@ -592,3 +605,55 @@ function($scope,  $q , $routeParams , egCore , egGridDataProvider , patronSvc ,
 }])
 
 
+.controller('BillHistoryCtrl',
+       ['$scope','$q','$routeParams','egCore','patronSvc','billSvc','egPromptDialog','$location',
+function($scope,  $q , $routeParams , egCore , patronSvc , billSvc , egPromptDialog , $location) {
+
+    $scope.initTab('bills', $routeParams.id);
+    billSvc.userId = $routeParams.id;
+    $scope.bill_tab = $routeParams.history_tab;
+    $scope.selected_billed = 0;
+    $scope.selected_paid = 0;
+
+    var start = new Date(); // now - 1 year
+    start.setFullYear(start.getFullYear() - 1),
+    $scope.dates = {
+        xact_start : start,
+        xact_finish : new Date()
+    }
+
+    $scope.xactQuery = function() {
+        // strip the time for our search purposes
+        var start = $scope.dates.xact_start.toISOString().replace(/T.*/,'');
+        var end = $scope.dates.xact_finish.toISOString().replace(/T.*/,'');
+        var today = new Date().toISOString().replace(/T.*/,'');
+
+        // open-ils.actor.user.transactions.history.have_bill_or_payment
+        var query = {
+            '-or' : [
+                {'summary.balance_owed' : {'<>' : 0}},
+                {'summary.last_payment_ts' : {'<>' : null}}
+            ],
+            xact_start : {'>=' : start},
+            usr : billSvc.userId
+        }
+
+        // end date of today implies that xacts with a null xact_finish
+        // are also acceptable
+        if (end < today) query.xact_finish = {'<=' : end};
+
+        return query;
+    }
+
+    $scope.showFullDetails = function(all) {
+        if (all[0]) 
+            $location.path('/circ/patron/' + 
+                patronSvc.current.id() + '/bill/' + all[0].id);
+    }
+    $scope.activateBill = function(xact) {
+        $scope.showFullDetails([xact]);
+    }
+
+}])
+
+
index 54c91c3..5c4ec2f 100644 (file)
@@ -208,8 +208,12 @@ angular.module('egCoreMod')
                 oncomplete : function() {
                     deferred.resolve(lastResp);
                 },
+                onmethoderror : function(e) {
+                    self.err(method + " failed " + e + "\n" + js2JSON(params));
+                    deferred.reject(e);
+                },
                 onerror : function(e) {
-                    self.err(method + " failed " + e);
+                    self.err(method + " failed " + e + "\n" + js2JSON(params));
                     deferred.reject(e);
                 }
             }).send();
index 6aec308..9ee72c7 100644 (file)
@@ -248,3 +248,29 @@ function($modal, $interpolate) {
     }
 })
 
+
+/*
+http://stackoverflow.com/questions/18061757/angular-js-and-html5-date-input-value-how-to-get-firefox-to-show-a-readable-d
+
+This directive allows us to use html5 input type="date" (for Chrome) and 
+gracefully fall back to a regular ISO text input for Firefox.
+It also allows us to abstract away some browser finickiness.
+*/
+.directive(
+    'egDateInput',
+    function(dateFilter) {
+        return {
+            require: 'ngModel',
+            template: '<input type="date"></input>',
+            replace: true,
+            link: function(scope, elm, attrs, ngModelCtrl) {
+                ngModelCtrl.$formatters.unshift(function (modelValue) {
+                    return dateFilter(modelValue, 'yyyy-MM-dd');
+                });
+                
+                ngModelCtrl.$parsers.unshift(function(viewValue) {
+                    return new Date(viewValue);
+                });
+            },
+        };
+})