LP#1356477: add quick receipt button
authorMike Rylander <miker@esilibrary.com>
Wed, 3 Aug 2016 20:51:54 +0000 (16:51 -0400)
committerMike Rylander <mrylander@gmail.com>
Wed, 24 Aug 2016 21:58:44 +0000 (17:58 -0400)
This patch adds a new button to the webstaff checkout page
called Quick Receipt. If the button itself is pushed, a
receipt containing the current checkouts is either printed
or emailed, depending on the user's preference. If the
drop-down portion of the button is used, staff members can
override the user's default preference to print or email
the receipt.

The Quick Receipt button is enabled only if at least one
checkout has been made during the current session.

Note that email receipts is an option only when the patron
has an email address supplied.

An icon next to the Quick Receipt button will be either a
printer or an envelope depending on the user's preferred
receipt setting.

This patch also modifies the "Done" button. If pressed, the session
is ended and the receipt is generated according to the user's
preferences, but the drop-down portion can be used to end
the session while letting the staff member choose how the
receipt is emitted.

If a receipt is emailed, a toast is displayed saying so.

Signed-off-by: Mike Rylander <miker@esilibrary.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/templates/staff/circ/patron/t_checkout.tt2
Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/checkout.js
Open-ILS/web/js/ui/default/staff/services/user.js
Open-ILS/web/js/ui/default/staff/test/karma.conf.js

index 309dd3d..0e936cf 100644 (file)
@@ -534,6 +534,7 @@ sub flesh_user {
         "cards",
         "card",
         "standing_penalties",
+        "settings",
         "addresses",
         "billing_address",
         "mailing_address",
@@ -3008,6 +3009,7 @@ sub user_retrieve_fleshed_by_id {
         "card",
         "groups",
         "standing_penalties",
+        "settings",
         "addresses",
         "billing_address",
         "mailing_address",
index 29974aa..8618419 100644 (file)
     </label>
   </div>
   <div class="pad-horiz">
-    <button class="btn btn-default" 
-      ng-click="print_receipt()">[% l('Print Receipt') %]</button>
+    <span ng-show="may_email_receipt()" class="glyphicon glyphicon-envelope" aria-label="[% l('Send Email Receipt') %]"></span>
+    <span ng-show="!may_email_receipt()" class="glyphicon glyphicon-print" aria-label="[% l('Print Receipt') %]"></span>
+    <div class="btn-group" uib-dropdown>
+      <button ng-click="print_or_email_receipt()" id="quick-button" type="button" ng-disabled="checkouts.length == 0" class="btn btn-default">[% l('Quick Receipt') %]</button>
+      <button type="button" ng-disabled="checkouts.length == 0" class="btn btn-default" uib-dropdown-toggle>
+        <span class="caret"></span>
+        <span class="sr-only">[% l('receipt option') %]</span>
+      </button>
+      <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="quick-button">
+        <li role="menuitem" ng-class="{disabled : !has_email_address()}"><a ng-click="email_receipt()" a-disabled="!has_email_address()" href="#">[% l('Email Receipt') %]</a></li>
+        <li role="menuitem"><a ng-click="print_receipt()" href="#">[% l('Print Receipt') %]</a></li>
+      </ul>
+    </div>
   </div>
-  <div>
+  <div class="btn-group" uib-dropdown>
     <button class="btn btn-default" 
-      ng-click="done()">[% l('Done') %]</button>
+      id="done-button" type="button"
+      ng-click="done_auto_receipt()">[% l('Done') %]</button>
+      <button type="button" class="btn btn-default" uib-dropdown-toggle>
+        <span class="caret"></span>
+        <span class="sr-only">[% l('receipt option') %]</span>
+      </button>
+      <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="done-button">
+        <li role="menuitem"><a ng-click="done_no_receipt()" href="#">[% l('No Receipt') %]</a></li>
+        <li role="menuitem" ng-class="{disabled : !has_email_address()}"><a ng-click="done_email_receipt()" a-disabled="!has_email_address()" href="#">[% l('Email Receipt') %]</a></li>
+        <li role="menuitem"><a ng-click="done_print_receipt()" href="#">[% l('Print Receipt') %]</a></li>
+      </ul>
   </div>
 </div>
 
index 316e64a..4f9c917 100644 (file)
@@ -12,6 +12,8 @@ s.CIRC_CLAIMS_RETURNED =
   '[% l('Item "[_1]" is marked as Claims Returned', '{{barcode}}') %]';
 s.CHECKOUT_FAILED_GENERIC =
   '[% l('Unable to checkout copy "[_1]" : [_2]', '{{barcode}}', '{{textcode}}') %]';
+s.EMAILED_CHECKOUT_RECEIPT =
+  "[% l('Emailed checkout receipt') %]";
 s.COPY_ALERT_MSG_DIALOG_TITLE =
   '[% l('Copy Alert Message for "[_1]"', '{{copy_barcode}}') %]';
 s.UNCAT_ALERT_DIALOG =
index fe13939..687dd3c 100644 (file)
@@ -5,7 +5,14 @@
  */
 
 angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 
-    'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod'])
+    'egCoreMod', 'egUiMod', 'egGridMod', 'egUserMod', 'ngToast'])
+
+.config(['ngToastProvider', function(ngToastProvider) {
+    ngToastProvider.configure({
+        verticalPosition: 'bottom',
+        animation: 'fade'
+    });
+}])
 
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
index 3d6e9c6..3f82de7 100644 (file)
@@ -5,10 +5,10 @@
 angular.module('egPatronApp').controller('PatronCheckoutCtrl',
 
        ['$scope','$q','$routeParams','egCore','egUser','patronSvc',
-        'egGridDataProvider','$location','$timeout','egCirc',
+        'egGridDataProvider','$location','$timeout','egCirc','ngToast',
 
 function($scope , $q , $routeParams , egCore , egUser , patronSvc , 
-         egGridDataProvider , $location , $timeout , egCirc) {
+         egGridDataProvider , $location , $timeout , egCirc , ngToast) {
 
     $scope.initTab('checkout', $routeParams.id).finally(function(){
         $scope.focusMe = true;
@@ -34,6 +34,34 @@ function($scope , $q , $routeParams , egCore , egUser , patronSvc ,
         );
     }
 
+    function setting_value (user, setting) {
+        if (user) {
+            var list = user.settings().filter(function(s){
+                return s.name() == setting;
+            });
+
+            if (list.length) return list[0].value();
+        }
+    }
+
+    $scope.has_email_address = function() {
+        return (
+            patronSvc.current &&
+            patronSvc.current.email() &&
+            patronSvc.current.email().match(/.*@.*/).length
+        );
+    }
+
+    $scope.may_email_receipt = function() {
+        return (
+            $scope.has_email_address() &&
+            setting_value(
+                patronSvc.current,
+                'circ.send_email_checkout_receipts'
+            ) == 'true'
+        );
+    }
+
     $scope.using_hatch = egCore.hatch.usingHatch();
 
     egCore.hatch.getItem('circ.checkout.strict_barcode')
@@ -191,18 +219,63 @@ function($scope , $q , $routeParams , egCore , egUser , patronSvc ,
         });
     }
 
-    // Redirect the user to the barcode entry page to load a new patron.
-    // If configured to do so, print the receipt first
-    $scope.done = function() {
-        if (printOnComplete) {
-
-            $scope.print_receipt().then(function() {
-                $location.path('/circ/patron/bcsearch');
+    $scope.email_receipt = function() {
+        if ($scope.has_email_address() && $scope.checkouts.length) {
+            return egCore.net.request(
+                'open-ils.circ',
+                'open-ils.circ.checkout.batch_notify.session.atomic',
+                egCore.auth.token(),
+                patronSvc.current.id(),
+                $scope.checkouts.map(function (c) { return c.circ.id() })
+            ).then(function() {
+                ngToast.create(egCore.strings.EMAILED_CHECKOUT_RECEIPT);
+                return $q.when();
             });
+        }
+        return $q.when();
+    }
+
+    $scope.print_or_email_receipt = function() {
+        if ($scope.may_email_receipt()) return $scope.email_receipt();
+        $scope.print_receipt();
+    }
 
+    // set of functions to issue a receipt (if desired), then
+    // redirect
+    $scope.done_auto_receipt = function() {
+        if ($scope.may_email_receipt()) {
+            $scope.email_receipt().then(function() {
+                $scope.done_redirect();
+            });
         } else {
-            $location.path('/circ/patron/bcsearch');
+            if (printOnComplete) {
+
+                $scope.print_receipt().then(function() {
+                    $scope.done_redirect();
+                });
+
+            } else {
+                $scope.done_redirect();
+            }
         }
     }
+    $scope.done_print_receipt = function() {
+        $scope.print_receipt().then( function () {
+            $scope.done_redirect();
+        });
+    }
+    $scope.done_email_receipt = function() {
+        $scope.email_receipt().then( function () {
+            $scope.done_redirect();
+        });
+    }
+    $scope.done_no_receipt = function() {
+        $scope.done_redirect();
+    }
+
+    // Redirect the user to the barcode entry page to load a new patron.
+    $scope.done_redirect = function() {
+        $location.path('/circ/patron/bcsearch');
+    }
 }])
 
index f2a70c1..9fd32a8 100644 (file)
@@ -11,6 +11,7 @@ function($q,  $timeout,  egNet,  egAuth,  egOrg) {
     var service = {
         defaultFleshFields : [
             'card',                                                                
+            'settings',
             'standing_penalties',                                                  
             'addresses',                                                           
             'billing_address',                                                     
index b8c1259..b0cef3f 100644 (file)
@@ -10,6 +10,8 @@ module.exports = function(config){
       'build/js/angular-route.min.js',
       'bower_components/angular-mocks/angular-mocks.js', // testing only
       'bower_components/angular-file-saver/dist/angular-file-saver.bundle.min.js',
+      'bower_components/ngtoast/dist/ngToast.min.js',
+      'bower_components/angular-sanitize/angular-sanitize.min.js',
       'build/js/ui-bootstrap.min.js',
       'build/js/hotkeys.min.js',
       'build/js/angular-cookies.min.js',