initial bills UI
authorBill Erickson <berick@esilibrary.com>
Tue, 20 May 2014 16:47:10 +0000 (12:47 -0400)
committerBill Erickson <berick@esilibrary.com>
Tue, 20 May 2014 16:47:10 +0000 (12:47 -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.tt2
Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/patron/t_search_results.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 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/services/grid.js

index ee09fa1..c17bce0 100644 (file)
@@ -19,6 +19,7 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/patron/checkout.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/patron/items_out.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/patron/holds.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/patron/bills.js"></script>
 
 <!-- TODO: APP_JS should really be called APP_ADDONS or some such.
     It just means "load these things, too, and load them last" -->
index d4e10ae..bc4750f 100644 (file)
@@ -1,6 +1,75 @@
-<div class="row pad-vert">
-  <div class="col-md-10 col-md-offset-1">
-    <div class="alert alert-warning">[% l('BILLS Development Pending') %]</div>
+
+<div class="row">
+  <div class="col-md-7">
+
+    <div class="row">
+      <div class="col-md-6">
+        <div class="strong-text-4">
+          [% l('Total Owed: [_1]', '{{summary.balance_owed() | currency}}') %]
+        </div>
+      </div>
+      <div class="col-md-6">
+        <!-- TODO -->
+        <div class="strong-text-2">
+          [% l('Refunds Available: [_1]', '{{0 | currency}}') %]
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-6">
+        <div class="strong-text-4">
+          [% l('Total Selected: [_1]', '{{total_selected | currency}}') %]
+        </div>
+      </div>
+      <div class="col-md-6">
+        <!-- FIXME: check patron credit display settings -->
+        <div class="strong-text-2">
+          [% l('Credit Available: [_1]', '{{patron().credit_forward_balance() | currency}}') %]
+        </div>
+      </div>
+    </div>
+  </div><!-- col -->
+
+  <div class="col-md-5">
+    <form role="form" class="form-horizontal" ng-submit="applyPayment()">
+      <fieldset>
+        <legend>[% l('Pay Bill') %]</legend>
+
+        <div class="form-group">
+          <label for="type-input" class="col-md-6 control-label">[% l('Payment Type') %]</label>
+          <div class="col-md-6">
+            <select ng-model="payment_type" class="form-control">
+              <option value="cash" selected="selected">[% l('Cash') %]</option>
+              <option value="check" selected="selected">[% l('Check') %]</option>
+            </select>
+          </div>
+        </div>
+        <div class="form-group">
+          <label for="amount-input" class="col-md-6 control-label">
+            [% l('Payment Received') %]
+          </label>
+          <div class="col-md-6">
+            <input type="number" min="0" id="amount-input" 
+              focus-me="focus_payment" value="0" class="form-control col-md-6 "/>
+          </div>
+        </div>
+        <div class="form-group pull-right">
+          <div class="col-md-12">
+            <input id="annotate-payment" type="checkbox" ng-model="annotate_payment"/>
+            <label for="annotate-payment" class="ccontrol-label">[% l('Annotate') %]</label>
+            <button ng-click="applyDistribution($event)">[% l('See Distribution') %]</button>
+            <button type="submit">[% l('Apply Payment') %]</button>
+          </div>
+        </div>
+
+      </fieldset>
+    </form>
   </div>
+
+</div>
+
+
+<div class="pad-vert">
+[% INCLUDE 'staff/circ/patron/t_bills_list.tt2' %]
 </div>
 
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2
new file mode 100644 (file)
index 0000000..192585c
--- /dev/null
@@ -0,0 +1,21 @@
+
+<eg-grid
+  idl-class="mobts"
+  query="gridQuery"
+  main-label="[% l('Patron Bills') %]"
+  on-item-retrieved="gridItemRetrieved"
+  on-item-selected="gridItemSelected"
+  persist-key="circ.patron.bills">
+  <eg-grid-field label="[% ('Balance Owed') %]" path='balance_owed'></eg-grid-field>
+  <eg-grid-field label="[% ('Bill #') %]" path='id'></eg-grid-field>
+  <eg-grid-field label="[% ('Start') %]" path='xact_start'></eg-grid-field>
+  <eg-grid-field label="[% ('Total Billed') %]" path='total_owed'></eg-grid-field>
+  <eg-grid-field label="[% ('Total Paid') %]" path='total_paid'></eg-grid-field>
+  <eg-grid-field label="[% ('Type') %]" path='xact_type'></eg-grid-field>
+  <eg-grid-field label="[% ('Title') %]" name='title' 
+    path='circulation.target_copy.call_number.record.simple_record.title'>
+  </eg-grid-field>
+  <!-- virtual field -->
+  <eg-grid-field label="[% ('Payment Pending') %]" name="payment_pending"></eg-grid-field>
+</eg-grid>
index 1b2f213..84e8792 100644 (file)
@@ -4,6 +4,7 @@
   main-label="[% l('Patron Search Results') %]"
   items-provider="patronSearchGridProvider"
   activate-item="activatePatron"
+  on-item-selected="gridItemSelected"
   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 3fd7cc6..0a4f794 100644 (file)
@@ -133,6 +133,31 @@ table.list tr.selected td {
   margin-left: 10px;
 }
 
+
+.strong-text {
+  font-weight: bold;
+}
+.strong-text-1 {
+  font-size: 110%;
+  font-weight: bold;
+}
+.strong-text-2 {
+  font-size: 120%;
+  font-weight: bold;
+}
+.strong-text-3 {
+  font-size: 130%;
+  font-weight: bold;
+}
+.strong-text-4 {
+  font-size: 140%;
+  font-weight: bold;
+}
+
+.currency-input {
+  width: 8em;
+}
+
 /* ----------------------------------------------------------------------
  * Grid
  * ---------------------------------------------------------------------- */
index b48d61d..7960624 100644 (file)
           <!-- otherwise, simply display the item value, which may 
                pass through datatype-specific filtering. -->
           <span ng-if="!col.template">
-            {{itemFieldValue(item, col)}}
+            {{itemFieldValue(item, col) | egGridValueFilter:col}}
           </span>
       </div>
     </div>
index f40b189..75a4874 100644 (file)
@@ -465,12 +465,10 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
         return {offset : 0};
     }
 
-    // show the user summary for the first selected user
-    provider.select = function(items) {
-        if (items[0]) {
-            var user = items[0];
-            patronSvc.setDefault(null, user);
-        }
+    // FIXME: use onItemSelect instead
+    $scope.gridItemSelected = function(item) {
+        if (item)  // null if de-selecting
+            patronSvc.setDefault(null, item); 
     }
 
     provider.get = function(offset, count) {
@@ -670,16 +668,6 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore,
 }])
 
 /**
- * Manages edit
- */
-.controller('PatronBillsCtrl',
-       ['$scope','$routeParams','egCore',
-function($scope,  $routeParams,  egCore) {
-    $scope.initTab('bills', $routeParams.id);
-}])
-
-
-/**
  * Manages messages
  */
 .controller('PatronMessagesCtrl',
diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js b/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js
new file mode 100644 (file)
index 0000000..fd55a6d
--- /dev/null
@@ -0,0 +1,57 @@
+
+/* Billing Service */
+
+angular.module('egPatronApp')
+
+.factory('billSvc', 
+       ['$q','egCore',
+function($q , egCore) {
+
+    var service = {};
+
+    // user billing summary
+    service.fetchSummary = function() {
+        return egCore.pcrud.retrieve(
+            'mous', service.userId, {}, {authoritative : true})
+        .then(function(summary) {return service.summary = summary})
+    }
+
+    return service;
+}])
+
+
+/**
+ * Manages Bills
+ */
+.controller('PatronBillsCtrl',
+       ['$scope','$q','$routeParams','egCore','egGridDataProvider','billSvc',
+function($scope,  $q , $routeParams,  egCore , egGridDataProvider , billSvc) {
+    $scope.initTab('bills', $routeParams.id);
+    billSvc.userId = $routeParams.id;
+
+    $scope.total_selected = 0;
+    $scope.payment_type = 'cash';
+    $scope.focus_payment = true;
+    $scope.annotate_payment = false;
+
+
+    var query = {usr : billSvc.userId, balance_owed : {'<>' : 0}};
+    $scope.gridQuery = function() {return query};
+    $scope.gridItemRetrieved = function(item) {
+        item.payment_pending = 0;
+    }
+
+    $scope.gridItemSelected = function(selected, deSelected) {
+        if (selected) {
+            $scope.total_selected += Number(selected.balance_owed);
+        } else {
+            $scope.total_selected += Number(deSelected.balance_owed);
+        }
+    }
+
+    billSvc.fetchSummary().then(function(s) {$scope.summary = s});
+
+}])
+
+
+
index 0ca633b..1d9baea 100644 (file)
@@ -67,6 +67,9 @@ angular.module('egGridMod',
             // are absorbed by the grid.
             onItemRetrieved : '=',
 
+            // function called with (selectedItem, deSelectedItem, allSelectedItems)
+            onItemSelected : '=',
+
             // function;  if set, row index values will be hyperlinked and
             // the onclick for an item will call activateItem with the item
             // as the argument.
@@ -119,7 +122,7 @@ angular.module('egGridMod',
 
                 // items needed only by the grid; remove from scope
                 angular.forEach( 
-                    ['idlClass', 'onItemRetrieved','persistKey'], 
+                    ['idlClass','onItemRetrieved','onItemSelected','persistKey'], 
                     function(field) {
                         grid[field] = $scope[field];
                         delete $scope[field];
@@ -152,8 +155,10 @@ angular.module('egGridMod',
                     grid.columnsProvider.hideAllColumns();
                 }
 
-                if ($scope.autoFields) {
+                if (!grid.indexField && grid.idlClass)
                     grid.indexField = egCore.idl.classes[grid.idlClass].pkey;
+
+                if ($scope.autoFields) {
                     if (grid.autoLabel)
                         $scope.mainLabel = egCore.idl.classes[grid.idlClass].label;
                     grid.columnsProvider.compileAutoColumns();
@@ -169,6 +174,12 @@ angular.module('egGridMod',
                         query : $scope.query
                     });
 
+                    if (!grid.dataProvider.query) {
+                        console.error(
+                            "Grid query is required when no data provider is supplied");
+                        return;
+                    }
+
                     $scope.$watch(
                         function() { return grid.dataProvider.query() },
                         function(newVal, oldVal) { 
@@ -214,6 +225,7 @@ angular.module('egGridMod',
                     // link columns to scope after loadConfig(), since it
                     // replaces the columns array.
                     $scope.columns = grid.columnsProvider.columns;
+                    //if (grid.selfManagedData) grid.collect();
                 });
             }
 
@@ -415,17 +427,6 @@ angular.module('egGridMod',
                 }
             }
 
-            grid.informSelection = function() {
-                var items = [];
-                angular.forEach(Object.keys($scope.selected),
-                    function(index) {
-                        items.push($scope.items[grid.indexOf(index)]);
-                    }
-                );
-                grid.dataProvider.select(items);
-            }
-
-
             // returns true if item1 appears in the list before item2;
             // false otherwise.  this is slightly more efficient that
             // finding the position of each then comparing them.
@@ -508,7 +509,14 @@ angular.module('egGridMod',
                     grid.lastSelectedItemIndex = index;
                 }
 
-                grid.informSelection();
+                var selected = grid.getSelectedItems();
+                if (grid.onItemSelected) {
+                    if ($scope.selected[index]) {
+                        grid.onItemSelected(item, null, selected);
+                    } else {
+                        grid.onItemSelected(null, item, selected);
+                    }
+                }
             }
 
             // Builds a sort expression from column sort priorities.
@@ -734,7 +742,12 @@ angular.module('egGridMod',
                         scope[field] = true;
                 }
             );
-            scope.template = element.html();
+
+            // any HTML content within the field is its custom template
+            var tmpl = element.html();
+            if (tmpl && !tmpl.match(/^\s*$/))
+                scope.template = tmpl
+
             egGridCtrl.columnsProvider.add(scope);
             scope.$destroy();
         }
@@ -1056,6 +1069,7 @@ angular.module('egGridMod',
                         return $q.when();
 
                     var query = provider.query();
+                    console.log(JSON.stringify(query));
                     if (!query || angular.equals(query, {})) 
                         return $q.when();
 
@@ -1063,7 +1077,8 @@ angular.module('egGridMod',
                     var queryFields = {}
                     angular.forEach(provider.columnsProvider.columns, 
                         function(col) {
-                            if (col.required || col.visible)
+                            // only query IDL-tracked columns
+                            if (col.datatype && (col.required || col.visible))
                                 queryFields[col.name] = col.path;
                         }
                     );
@@ -1242,6 +1257,8 @@ angular.module('egGridMod',
             case 'timestamp':                                                  
                 // canned angular date filter FTW                              
                 return $filter('date')(value, 'shortDate');                                 
+            case 'money':                                                  
+                return $filter('currency')(value);
             default:                                                           
                 return value;                                                  
         }