LP#1841270: add Title Hold option in various places in staff client
authorJason Etheridge <jason@EquinoxInitiative.org>
Fri, 19 Jul 2019 22:59:19 +0000 (18:59 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Fri, 6 Sep 2019 21:58:01 +0000 (17:58 -0400)
* Adds a Title Hold option to certain invocations of Request Items
(Item Status, Copy Buckets, but not Holdings View)

* Switches count of items to count of titles when Title hold option
is selected

* Adds a checkbox for honoring the preferred notification settings
and default pickup library of the selected patron

* Adds a success/failure toast for Request Items

Signed-off-by: Jason Etheridge <jason@EquinoxInitiative.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
Open-ILS/src/templates/staff/cat/bucket/copy/index.tt2
Open-ILS/src/templates/staff/cat/catalog/t_request_items.tt2
Open-ILS/src/templates/staff/cat/item/index.tt2
Open-ILS/web/js/ui/default/staff/cat/bucket/copy/app.js
Open-ILS/web/js/ui/default/staff/cat/item/app.js
Open-ILS/web/js/ui/default/staff/circ/services/item.js

index 1336e4e..e19915e 100644 (file)
@@ -108,6 +108,36 @@ sub test_and_create_hold_batch {
 
             $params->{'depth'} = $res->{'depth'} if $res->{'depth'};
 
+            if ($$oargs{honor_user_settings}) {
+                my $recipient = $e->retrieve_actor_user($$params{patronid})
+                    or return $e->die_event;
+                my $opac_hold_notify = $e->search_actor_user_setting(
+                    {usr => $$params{patronid}, name => 'opac.hold_notify'})->[0];
+                if ($opac_hold_notify) {
+                    if ($opac_hold_notify->value =~ 'email') {
+                        $$params{email_notify} = 1;
+                    }
+                    if ($opac_hold_notify->value =~ 'phone') {
+                        my $opac_default_phone = $e->search_actor_user_setting(
+                            {usr => $$params{patronid}, name => 'opac.default_phone'})->[0];
+                        # FIXME - what's up with the ->value putting quotes around the string?
+                        if ($opac_default_phone && $opac_default_phone->value =~ /^"(.*)"$/) {
+                            $$params{phone_notify} = $1;
+                        }
+                    }
+                    if ($opac_hold_notify->value =~ 'sms') {
+                        my $opac_default_sms_carrier = $e->search_actor_user_setting(
+                            {usr => $$params{patronid}, name => 'opac.default_sms_carrier'})->[0];
+                        $$params{sms_carrier} = $opac_default_sms_carrier->value if $opac_default_sms_carrier;
+                        my $opac_default_sms_notify = $e->search_actor_user_setting(
+                            {usr => $$params{patronid}, name => 'opac.default_sms_notify'})->[0];
+                        if ($opac_default_sms_notify && $opac_default_sms_notify->value =~ /^"(.*)"$/) {
+                            $$params{sms_notify} = $1;
+                        }
+                    }
+                }
+            }
+
             # Remove oargs from params so holds can be created.
             if ($$params{oargs}) {
                 delete $$params{oargs};
index 91d2f20..74a379f 100644 (file)
       "[% l('One or more items could not be transferred. Override?') %]";
     s.OVERRIDE_TRANSFER_COPY_BUCKET_ITEMS_TO_MARKED_VOLUME_BODY =
       "[% l('Reason(s) include: [_1]', '{{evt_desc}}') %]";
+    s.SUCCESS_HOLD_REQUEST =
+      "[% l('Hold successfully requested') %]";
+    s.FAILURE_HOLD_REQUEST =
+      "[% l('Hold not successfully requested') %]";
   }])
 </script>
 [% END %]
index f28a521..15fba04 100644 (file)
       </div>
       <div class="col-md-6"> {{user_name}} </div>
     </div>
+    <div class="row">
+      <div class="col-md-6">
+          <label>
+            <input type="checkbox" ng-model="hold_data.honor_user_settings"/>
+            [% l('Honor user preferences?') %]
+          </label>
+      </div>
+      <div></div>
+    </div>
     <div class="row pad-vert">
       <div class="col-md-6">
         <div class="input-group">
@@ -23,6 +32,7 @@
             <option value="C">[% l('Copy Hold') %]</option>
             <option value="R" selected>[% l('Recall Hold') %]</option>
             <option value="F">[% l('Force Hold') %]</option>
+            <option ng-show="hold_data.record_list" value="T">[% l('Title Hold') %]</option>
           </select>
         </div>
       </div>
   </div>
   <div class="modal-footer">
     <div class="row">
-      <div class="col-md-6">
+      <div ng-show="hold_data.copy_list && hold_data.hold_type != 'T'" class="col-md-6">
         [% l('Number of items: ') %] {{hold_data.copy_list.length}}
       </div>
+      <div ng-show="hold_data.record_list && hold_data.hold_type == 'T'" class="col-md-6">
+        [% l('Number of titles: ') %] {{hold_data.record_list.length}}
+      </div>
       <div class="col-md-6 pull-right">
         <input type="submit" class="btn btn-primary" value="[% l('OK') %]" ng-disabled="!hold_data.user"/>
         <button class="btn btn-warning" ng-click="cancel($event)">[% l('Cancel') %]</button>
index 6af18a6..9cb8be8 100644 (file)
       "[% l('Item successfully modified') %]";
     s.ITEMS_SUCCESSFULLY_MODIFIED =
       "[% l('Item(s) successfully modified') %]";
+    s.SUCCESS_HOLD_REQUEST =
+      "[% l('Hold successfully requested') %]";
+    s.FAILURE_HOLD_REQUEST =
+      "[% l('Hold not successfully requested') %]";
 
   }])
 </script>
index f23b8a9..e5cc7f4 100644 (file)
@@ -514,10 +514,10 @@ function($scope,  $routeParams,  bucketSvc , egGridDataProvider,   egCore) {
 }])
 
 .controller('ViewCtrl',
-       ['$scope','$q','$routeParams','$timeout','$window','$uibModal','bucketSvc','egCore','egUser',
-        'egConfirmDialog',
-function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc , egCore , egUser ,
-         egConfirmDialog) {
+       ['$scope','$q','$routeParams','$timeout','$window','$uibModal','bucketSvc','egCore','egOrg','egUser',
+        'ngToast','egConfirmDialog',
+function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc , egCore , egOrg , egUser ,
+         ngToast , egConfirmDialog) {
 
     $scope.setTab('view');
     $scope.bucketId = $routeParams.id;
@@ -621,6 +621,13 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
                 return i.id;
             }
         );
+        var record_list = $scope.gridControls.selectedItems().map(
+            function (i) {
+                return i['call_number.record.id'];
+            }
+        ).filter(function(v,i,s){ // dedup
+            return s.indexOf(v) == i;
+        });
 
         if (copy_list.length == 0) return;
 
@@ -637,8 +644,11 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
                 $scope.hold_data = {
                     hold_type : 'C',
                     copy_list : copy_list,
+                    record_list : record_list,
                     pickup_lib: egCore.org.get(egCore.auth.user().ws_ou()),
-                    user      : egCore.auth.user().id()
+                    user      : egCore.auth.user().id(),
+                    honor_user_settings : 
+                        egCore.hatch.getLocalItem('eg.cat.request_items.honor_user_settings')
                 };
 
                 egUser.get( $scope.hold_data.user ).then(function(u) {
@@ -650,12 +660,24 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
 
                 $scope.user_name = '';
                 $scope.barcode = '';
+                function user_preferred_pickup_lib(u) {
+                    var pickup_lib = u.home_ou();
+                    angular.forEach(u.settings(), function (s) {
+                        if (s.name() == "opac.default_pickup_location") {
+                            pickup_lib = s.value();
+                        }
+                    });
+                    return egOrg.get(pickup_lib);
+                }
                 $scope.$watch('barcode', function (n) {
                     if (!$scope.first_user_fetch) {
                         egUser.getByBarcode(n).then(function(u) {
                             $scope.user = u;
                             $scope.user_name = egUser.format_name(u);
                             $scope.hold_data.user = u.id();
+                            if ($scope.hold_data.honor_user_settings) {
+                                $scope.hold_data.pickup_lib = user_preferred_pickup_lib(u);
+                            }
                         }, function() {
                             $scope.user = null;
                             $scope.user_name = '';
@@ -664,6 +686,14 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
                     }
                     $scope.first_user_fetch = false;
                 });
+                $scope.$watch('hold_data.honor_user_settings', function (n) {
+                    if (n && $scope.user) {
+                        $scope.hold_data.pickup_lib = user_preferred_pickup_lib($scope.user);
+                    } else {
+                        $scope.hold_data.pickup_lib = egCore.org.get(egCore.auth.user().ws_ou());
+                    }
+                    egCore.hatch.setLocalItem('eg.cat.request_items.honor_user_settings',n);
+                });
 
                 $scope.ok = function(h) {
                     var args = {
@@ -676,8 +706,25 @@ function($scope,  $q , $routeParams , $timeout , $window , $uibModal , bucketSvc
                     egCore.net.request(
                         'open-ils.circ',
                         'open-ils.circ.holds.test_and_create.batch.override',
-                        egCore.auth.token(), args, h.copy_list
-                    );
+                        egCore.auth.token(), args,
+                        h.hold_type == 'T' ? h.record_list : h.copy_list,
+                        { 'all' : 1, 'honor_user_settings' : h.honor_user_settings }
+                    ).then(function(r) {
+                        console.log('request result',r);
+                        if (isNaN(r.result)) {
+                            if (typeof r.result.desc != 'undefined') {
+                                ngToast.danger(r.result.desc);
+                            } else {
+                                if (typeof r.result.last_event != 'undefined') {
+                                    ngToast.danger(r.result.last_event.desc);
+                                } else {
+                                    ngToast.danger(egCore.strings.FAILURE_HOLD_REQUEST);
+                                }
+                            }
+                        } else {
+                            ngToast.success(egCore.strings.SUCCESS_HOLD_REQUEST);
+                        }
+                    });
 
                     $uibModalInstance.close();
                 }
index b861801..52d2d02 100644 (file)
@@ -146,7 +146,7 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
     }
 
     $scope.requestItems = function() {
-        itemSvc.requestItems([$scope.args.copyId]);
+        itemSvc.requestItems([$scope.args.copyId],[$scope.args.recordId]);
     }
 
     $scope.update_inventory = function() {
@@ -538,7 +538,8 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
 
     $scope.requestItems = function() {
         var copy_list = gatherSelectedHoldingsIds();
-        itemSvc.requestItems(copy_list);
+        var record_list = gatherSelectedRecordIds();
+        itemSvc.requestItems(copy_list,record_list);
     }
 
     $scope.replaceBarcodes = function() {
index eda4d8e..8e21428 100644 (file)
@@ -4,8 +4,8 @@
 
 angular.module('egCoreMod')
     .factory('egItem',
-       ['egCore','egCirc','$uibModal','$q','$timeout','$window','egConfirmDialog','egAlertDialog',
-function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog , egAlertDialog ) {
+       ['egCore','egOrg','egCirc','$uibModal','$q','$timeout','$window','ngToast','egConfirmDialog','egAlertDialog',
+function(egCore , egOrg , egCirc , $uibModal , $q , $timeout , $window , ngToast , egConfirmDialog , egAlertDialog ) {
 
     var service = {
         copies : [], // copy barcode search results
@@ -358,8 +358,13 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
         location.href = "/eg2/staff/booking/manage_reservations/by_resource/" + barcode;
     }
 
-    service.requestItems = function(copy_list) {
+    service.requestItems = function(copy_list,record_list) {
         if (copy_list.length == 0) return;
+        if (record_list) {
+            record_list = record_list.filter(function(v,i,s){ // dedup
+                return s.indexOf(v) == i;
+            });
+        }
 
         return $uibModal.open({
             templateUrl: './cat/catalog/t_request_items',
@@ -374,8 +379,11 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
                 $scope.hold_data = {
                     hold_type : 'C',
                     copy_list : copy_list,
+                    record_list : record_list,
                     pickup_lib: egCore.org.get(egCore.auth.user().ws_ou()),
-                    user      : egCore.auth.user().id()
+                    user      : egCore.auth.user().id(),
+                    honor_user_settings : 
+                        egCore.hatch.getLocalItem('eg.cat.request_items.honor_user_settings')
                 };
 
                 egUser.get( $scope.hold_data.user ).then(function(u) {
@@ -387,12 +395,24 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
 
                 $scope.user_name = '';
                 $scope.barcode = '';
+                function user_preferred_pickup_lib(u) {
+                    var pickup_lib = u.home_ou();
+                    angular.forEach(u.settings(), function (s) {
+                        if (s.name() == "opac.default_pickup_location") {
+                            pickup_lib = s.value();
+                        }
+                    });
+                    return egOrg.get(pickup_lib);
+                }
                 $scope.$watch('barcode', function (n) {
                     if (!$scope.first_user_fetch) {
                         egUser.getByBarcode(n).then(function(u) {
                             $scope.user = u;
                             $scope.user_name = egUser.format_name(u);
                             $scope.hold_data.user = u.id();
+                            if ($scope.hold_data.honor_user_settings) {
+                                $scope.hold_data.pickup_lib = user_preferred_pickup_lib(u);
+                            }
                         }, function() {
                             $scope.user = null;
                             $scope.user_name = '';
@@ -401,6 +421,14 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
                     }
                     $scope.first_user_fetch = false;
                 });
+                $scope.$watch('hold_data.honor_user_settings', function (n) {
+                    if (n && $scope.user) {
+                        $scope.hold_data.pickup_lib = user_preferred_pickup_lib($scope.user);
+                    } else {
+                        $scope.hold_data.pickup_lib = egCore.org.get(egCore.auth.user().ws_ou());
+                    }
+                    egCore.hatch.setLocalItem('eg.cat.request_items.honor_user_settings',n);
+                });
 
                 $scope.ok = function(h) {
                     var args = {
@@ -413,8 +441,25 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
                     egCore.net.request(
                         'open-ils.circ',
                         'open-ils.circ.holds.test_and_create.batch.override',
-                        egCore.auth.token(), args, h.copy_list
-                    );
+                        egCore.auth.token(), args,
+                        h.hold_type == 'T' ? h.record_list : h.copy_list,
+                        { 'all' : 1, 'honor_user_settings' : h.honor_user_settings }
+                    ).then(function(r) {
+                        console.log('request result',r);
+                        if (isNaN(r.result)) {
+                            if (typeof r.result.desc != 'undefined') {
+                                ngToast.danger(r.result.desc);
+                            } else {
+                                if (typeof r.result.last_event != 'undefined') {
+                                    ngToast.danger(r.result.last_event.desc);
+                                } else {
+                                    ngToast.danger(egCore.strings.FAILURE_HOLD_REQUEST);
+                                }
+                            }
+                        } else {
+                            ngToast.success(egCore.strings.SUCCESS_HOLD_REQUEST);
+                        }
+                    });
 
                     $uibModalInstance.close();
                 }