offline: hotkeys, block reasons, optional bypass of barcode errors, pending xacts...
authorMike Rylander <mrylander@gmail.com>
Mon, 12 Jun 2017 21:12:38 +0000 (17:12 -0400)
committerMike Rylander <mrylander@gmail.com>
Mon, 12 Jun 2017 21:13:16 +0000 (17:13 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/templates/staff/base_js.tt2
Open-ILS/src/templates/staff/index.tt2
Open-ILS/src/templates/staff/navbar.tt2
Open-ILS/src/templates/staff/offline-interface.tt2
Open-ILS/src/templates/staff/t_login.tt2
Open-ILS/web/js/ui/default/staff/app.js
Open-ILS/web/js/ui/default/staff/offline.js
Open-ILS/web/js/ui/default/staff/services/lovefield.js

index 73a5429..8c65a80 100644 (file)
@@ -169,7 +169,9 @@ UpUp.start({
     s.PATRON_NOT_FOUND = "[% l('Patron not found') %]";
     s.PATRON_BLOCKED = "[% l('Patron blocked') %]";
     s.BAD_BARCODE = "[% l('Bad item barcode') %]";
+    s.BAD_BARCODE_CD = "[% l('Item barcode does not have a correct check digit.') %]";
     s.BAD_PATRON_BARCODE = "[% l('Bad patron barcode') %]";
+    s.BAD_PATRON_BARCODE_CD = "[% l('Patron barcode does not have a correct check digit.') %]";
     s.ITEM_NOT_FOUND = "[% l('Item not found') %]";
     s.CONFIRM_CLEAR_PENDING = "[% l('Clear pending transactions') %]";
     s.CONFIRM_CLEAR_PENDING_BODY = "[% l('Are you certain you want to clear these pending offline transactions? This action is irreversible. Transactions cannot be recovered after clearing!') %]";
index 803774f..bcb7a14 100644 (file)
@@ -6,6 +6,7 @@
 
 [% BLOCK APP_JS %]
 <!-- splash / login page app -->
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/egLovefield.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/app.js"></script>
 [% END %]
 
index e443d3a..e19e22a 100644 (file)
@@ -68,7 +68,7 @@
         </a>
 
         <ul uib-dropdown-menu>
-          <li>
+          <li ng-if="username">
             <a href="./circ/patron/bcsearch" target="_self"
               eg-accesskey="[% l('f1') %]" 
               eg-accesskey-desc="[% l('Check Out') %]">
               [% l('Check Out') %]
             </a>
           </li>
-          <li>
+          <li ng-if="!username">
+            <a href="" ng-click="rs.active_tab('checkout')" target="_self"
+              eg-accesskey="[% l('f1') %]" 
+              eg-accesskey-desc="[% l('Check Out') %]">
+              <span class="glyphicon glyphicon-export"></span>
+              [% l('Check Out') %]
+            </a>
+          </li>
+          <li ng-if="username">
             <a href="./circ/checkin/checkin" target="_self"
               eg-accesskey="[% l('f2') %]" 
               eg-accesskey-desc="[% l('Check In') %]">
               [% l('Check In') %]
             </a>
           </li>
+          <li ng-if="!username">
+            <a href="" ng-click="rs.active_tab('checkin')" target="_self"
+              eg-accesskey="[% l('f2') %]" 
+              eg-accesskey-desc="[% l('Check In') %]">
+              <span class="glyphicon glyphicon-import"></span>
+              [% l('Check In') %]
+            </a>
+          </li>
           <li>
             <a href="./circ/checkin/capture" target="_self"
               eg-accesskey="[% l('shift+f2') %]" 
               [% l('Pull List for Hold Requests') %]
             </a>
           </li>
-          <li>
+          <li ng-if="username">
             <a href="./circ/renew/renew" target="_self"
               eg-accesskey="[% l('ctrl+f2') %]" 
               eg-accesskey-desc="[% l('Renew items') %]">
               [% l('Renew Items') %]
             </a>
           </li>
-          <li>
+          <li ng-if="!username">
+            <a href="" ng-click="rs.active_tab('renew')" target="_self"
+              eg-accesskey="[% l('ctrl+f2') %]" 
+              eg-accesskey-desc="[% l('Renew items') %]">
+              <span class="glyphicon glyphicon-refresh"></span>
+              [% l('Renew Items') %]
+            </a>
+          </li>
+          <li ng-if="username">
             <a href="./circ/patron/register" target="_self"
               eg-accesskey="[% l('shift+f1') %]" 
               eg-accesskey-desc="[% l('Register Patron') %]">
               [% l('Register Patron') %]
             </a>
           </li>
+          <li ng-if="!username">
+            <a href="" ng-click="rs.active_tab('register')" target="_self"
+              eg-accesskey="[% l('shift+f1') %]" 
+              eg-accesskey-desc="[% l('Register Patron') %]">
+              <span class="glyphicon glyphicon-user"></span>
+              [% l('Register Patron') %]
+            </a>
+          </li>
           <li>
             <a href="./circ/patron/last" target="_self"
               eg-accesskey="[% l('f8') %]" 
               <span>[% l('Verify Credentials') %]</span>
             </a>
           </li>
-          <li>
+          <li ng-if="username">
             <a href="./circ/in_house_use/index" target="_self"
               eg-accesskey="[% l('f6') %]" 
               eg-accesskey-desc="[% l('Record In-House Use') %]">
               <span>[% l('Record In-House Use') %]</span>
             </a>
           </li>
+          <li ng-if="!username">
+            <a href="" ng-click="rs.active_tab('in_house_use')" target="_self"
+              eg-accesskey="[% l('f6') %]" 
+              eg-accesskey-desc="[% l('Record In-House Use') %]">
+              <span class="glyphicon glyphicon-pencil"></span>
+              <span>[% l('Record In-House Use') %]</span>
+            </a>
+          </li>
           <li>
             <a href="./circ/holds/shelf" target="_self">
               <span class="glyphicon glyphicon-tasks"></span>
index 49451e5..caaa078 100644 (file)
   </div>
 </div>
 
+<div class="row col-md-offset-3 col-md-6 pad-vert">
+  <div ng-show="logged_in && active_tab != 'session'" class="alert alert-danger">
+    <h2>[% l('Warning') %]</h2>
+    [% l('You are currently logged in while attempting to use the offline interface.  If use of this interface is intended, or if you were redirected here due to an internet or server outage, please use the Log Out action in the above-right menu and then select Offline Circulation from the Circulation menu') %]
+  </div>
+</div>
+
 <div class="row col-md-12 pad-vert">
   <div class="col-md-12">
     <uib-tabset active="active_tab">
@@ -594,11 +601,22 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
   s.OFFLINE_BLOCKLIST_SUCCESS = "[% l('Offline blocklist downloaded') %]";
   s.OFFLINE_BLOCKLIST_FAIL = "[% l('Error downloading offline blocklist') %]";
   s.DUPLICATE_BARCODE = "[% l('Duplicate item barcode') %]";
+
+  s.ALLOW = "[% l('Allow') %]";
+  s.REJECT = "[% l('Reject') %]";
+
   s.REG_ADDR_TYPE = "[% l('Mailing') %]";
   s.REG_INVALID_FIELDS =
     "[% l('Please enter valid values for all required fields.') %]"
   s.REG_ADDR_REQUIRED =
     "[% l('An address is required during registration.') %]"
+
+  s.PATRON_BLOCKED_WHY = {};
+  s.PATRON_BLOCKED_WHY.D = "[% l('Patron has penalties') %]";
+  s.PATRON_BLOCKED_WHY.L = "[% l('Barcode is reported Lost') %]";
+  s.PATRON_BLOCKED_WHY.E = "[% l('Patron account is Expired') %]";
+  s.PATRON_BLOCKED_WHY.B = "[% l('Patron account is Barred') %]";
+
 }]);
 </script>
 <link rel="stylesheet" href="[% ctx.base_path %]/staff/css/circ.css" />
index ce06e6b..e4d42cb 100644 (file)
               </div>
             </div>
 
+            <div class="form-group">
+              <div class="col-md-offset-4 col-md-6">
+                <span ng-show="pendingXacts" class="label label-warning">[% l('Unprocessed offline transactions waiting for upload') %]</span>
+              </div>
+            </div>
+
           </form>
         </fieldset>
       </div>
index 4cf388c..f76c278 100644 (file)
@@ -40,8 +40,12 @@ function($routeProvider , $locationProvider) {
     /* inject services into our controller.  Spelling them
      * out like this allows the auto-magic injector to work
      * even if the code has been minified */
-           ['$scope','$location','$window','egCore',
-    function($scope , $location , $window , egCore) {
+           ['$scope','$location','$window','egCore','egLovefield',
+    function($scope , $location , $window , egCore , egLovefield) {
+        egLovefield.havePendingOfflineXacts() .then(function(eh){
+            $scope.pendingXacts = eh;
+        });
+
         $scope.focusMe = true;
         $scope.args = {};
         $scope.workstations = [];
index c924d64..02642c1 100644 (file)
@@ -371,7 +371,7 @@ function($routeProvider , $locationProvider , $compileProvider) {
                         });
                     }
                 },function(){
-                    ngToast.create(egCore.strings.OFFLINE_BLOCKLIST_FAIL);
+                    ngToast.warning(egCore.strings.OFFLINE_BLOCKLIST_FAIL);
                     egCore.audio.play('warning.offline.blocklist_fail');
                 }
             );
@@ -430,6 +430,7 @@ function($routeProvider , $locationProvider , $compileProvider) {
         }
 
         $rootScope.save_offline_xacts = function () { return $scope.save() };
+        $rootScope.active_tab = function (t) { $scope.active_tab = t };
 
         $scope.clear_pending = function (skip_confirm) {
             if (skip_confirm) {
@@ -539,15 +540,27 @@ function($routeProvider , $locationProvider , $compileProvider) {
             if (pbarcode) {
                 egLovefield.testOfflineBlock(pbarcode).then(function (blocked) {
                     if (blocked) {
-                        $scope.blocked_patron = xtype;
-                        ngToast.warning(egCore.strings.PATRON_BLOCKED);
                         egCore.audio.play('warning.offline.blocked_patron');
+                        egConfirmDialog.open(
+                            egCore.strings.PATRON_BLOCKED,
+                            egCore.strings.PATRON_BLOCKED_WHY[blocked],
+                            {}, egCore.strings.ALLOW, egCore.strings.REJECT
+                        ).result.then(
+                            function(){ // forced
+                                $scope.blocked_patron = null;
+                                _add_impl(xtype,true)
+                                if (next_focus) $('#'+next_focus).focus();
+                            },function(){ // stopped
+                                $scope.blocked_patron = xtype;
+                                if (next_focus) $('#'+next_focus).focus();
+                                return;
+                            }
+                        );
+                    } else {
+                        $scope.blocked_patron = null;
+                        _add_impl(xtype,true)
                         if (next_focus) $('#'+next_focus).focus();
-                        return;
                     }
-                    $scope.blocked_patron = null;
-                    _add_impl(xtype,true)
-                    if (next_focus) $('#'+next_focus).focus();
                 });
             } else {
                 _add_impl(xtype);
@@ -562,21 +575,48 @@ function($routeProvider , $locationProvider , $compileProvider) {
             if ($scope.strict_barcode && pbarcode) {
                 if (!check_barcode(pbarcode)) {
                     $scope.bad_barcode = xtype;
-                    ngToast.warning(egCore.strings.BAD_PATRON_BARCODE);
                     egCore.audio.play('warning.offline.bad_barcode');
-                    return;
+                    return egConfirmDialog.open(
+                        egCore.strings.BAD_PATRON_BARCODE,
+                        egCore.strings.BAD_PATRON_BARCODE_CD,
+                        {}, egCore.strings.ALLOW, egCore.strings.REJECT
+                    ).result.then(
+                        function(){ // forced
+                            $scope.blocked_patron = null;
+                            return _add_impl2(xtype,digest)
+                        },function(){ // stopped
+                            $scope.blocked_patron = xtype;
+                        }
+                    );
                 }
             }
 
             if ($scope.strict_barcode && $scope[xtype].barcode) {
                 if (!check_barcode($scope[xtype].barcode)) {
                     $scope.bad_barcode = xtype;
-                    ngToast.warning(egCore.strings.BAD_BARCODE);
                     egCore.audio.play('warning.offline.bad_barcode');
-                    return;
+                    return egConfirmDialog.open(
+                        egCore.strings.BAD_BARCODE,
+                        egCore.strings.BAD_BARCODE_CD,
+                        {}, egCore.strings.ALLOW, egCore.strings.REJECT
+                    ).result.then(
+                        function(){ // forced
+                            $scope.blocked_patron = null;
+                            return _add_impl2(xtype,digest)
+                        },function(){ // stopped
+                            $scope.blocked_patron = xtype;
+                        }
+                    );
                 }
             }
 
+            return _add_impl2(xtype,digest);
+        }
+
+        function _add_impl2 (xtype,digest) {
+            var pbarcode = $scope[xtype].patron_barcode;
+            var backdate = $scope[xtype].backdate;
+
             $scope.bad_barcode = null;
 
             var now = new Date().getTime();
@@ -603,7 +643,7 @@ function($routeProvider , $locationProvider , $compileProvider) {
             if (backdate) $scope[xtype].backdate = backdate;
             if (xtype=="in_house_use") $scope[xtype].count = 1;
 
-            if (digest) $scope.$apply();
+            if (digest) $timeout(function(){$scope.$apply()});
         }
 
         check_barcode = function(bc) {
index 7e13a18..a05e824 100644 (file)
@@ -104,6 +104,19 @@ angular.module('egCoreMod')
         });
     }
 
+    service.havePendingOfflineXacts = function () {
+        return connectOrGo(function() {
+            var table = lf.offlineDB.getSchema().table('OfflineXact');
+            return lf.offlineDB.
+                select(table.reason).
+                from(table).
+                exec().
+                then(function(list) {
+                    return $q.when(Boolean(list.length > 0))
+                });
+        });
+    }
+
     service.retrievePendingOfflineXacts = function () {
         return connectOrGo(function() {
             var table = lf.offlineDB.getSchema().table('OfflineXact');
@@ -147,11 +160,12 @@ angular.module('egCoreMod')
         return connectOrGo(function() {
             var table = lf.offlineDB.getSchema().table('OfflineBlocks');
             return lf.offlineDB.
-                select().
+                select(table.reason).
                 from(table).
                 where(table.barcode.eq(barcode)).
                 exec().then(function(list) {
-                    return $q.when(Boolean(list.length > 0));
+                    if(list.length > 0) return $q.when(list[0].reason);
+                    return $q.when(null);
                 });
         });
     }