LP##1661688 - Want easy way to clear a hold when picked up by other patron.
authorDan Pearl <dpearl@cwmars.org>
Tue, 24 Jan 2017 18:59:01 +0000 (13:59 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Wed, 28 Feb 2018 21:48:58 +0000 (16:48 -0500)
This common action was handled previously as
    1) item is scanned - The system detects that this items is for a different
       patron, and an error message is displayed.

    2) Force Override was selected - If the librarian had the appropriate
       privileges, the Force Override could allow the checkout to occur.

    3) librarian cancels original hold - This independent operation would
       be needed to cancel the original hold, otherwise the hold would be
       targeted again.

This feature provides the option to cancel the hold in step #2. An error
message will be displayed as before, but within that message will be a checkbox to allow cancelling of the original hold which, if checked, will mean that step #3 will be automatically done.

The checked/non-checked state of the checkbox is initialized with an
organizational unit setting. The default is false (to preserve default
prior behavior).

Note: This change only affects the "web client".

TO TEST THIS

A. Before the Patch

Hold an item for patron "A".  Attempt to check it out for patron "B".
Observe the choices to Force Override (to allow the checkout) or to cancel.
Do a Force Override and observe that the original hold is still active.

B. After the Patch

Repeat the above scenario, and observe the (blank) checkbox.  Perform the
Force Override and observe that the original hold is still active.

Repeat the scenario, except this time check the checkbox.  Observe after the checheckout is done that original hold is gone.

Adjust the organizational setting (see the Release Notes) to change the default
appearance of the checkbox.

For completeness, you can verify that if you cancel out the message pop-up that the checkout is cancelled and and that the hold status is unchanged, regardless of the state of the checkbox.

Signed-off-by: Dan Pearl <dpearl@cwmars.org>
Signed-off-by: Jason Stephenson <jason@sigio.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.hold_checkout.sql [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/share/t_event_override_dialog.tt2
Open-ILS/web/js/ui/default/staff/circ/services/circ.js
docs/RELEASE_NOTES_NEXT/Circulation/alternate_patron_hold_pickup.adoc [new file with mode: 0644]

index 8dbfcc2..f516381 100644 (file)
@@ -1119,7 +1119,12 @@ sub check_captured_holds {
                 cancel_time         => undef, 
                 fulfillment_time    => undef 
             },
-            { limit => 1 }
+            { limit => 1,
+              flesh => 2,
+              flesh_fields => { ahr => ['usr'],
+                                au => ['family_name', 'first_given_name']
+             }
+            }
         ]
     )->[0];
 
@@ -1130,7 +1135,17 @@ sub check_captured_holds {
 
     $logger->info("circulator: this copy is needed by a different patron to fulfill a hold");
 
-    $self->push_events(OpenILS::Event->new('ITEM_ON_HOLDS_SHELF'));
+    my $payload;
+    my $holdau = $hold->usr;
+
+    if ($holdau) {
+       $payload->{patron_name} = $holdau->first_given_name . ' ' . $holdau->family_name;
+    } else {
+       $payload->{patron_name} = "???";
+    }
+    $payload->{hold_id}     = $hold->id;
+    $self->push_events(OpenILS::Event->new('ITEM_ON_HOLDS_SHELF',
+           payload => $payload));
 }
 
 
index ab4468d..43bf6ae 100644 (file)
@@ -16729,6 +16729,29 @@ INSERT INTO config.org_unit_setting_type
 INSERT INTO config.org_unit_setting_type
 ( name, grp, label, description, datatype )
 VALUES
+       ('circ.clear_hold_on_checkout',
+        'circ',
+       oils_i18n_gettext('circ.clear_hold_on_checkout',
+               'Clear hold when other patron checks out item',
+               'coust', 'label'),
+        oils_i18n_gettext('circ.clear_hold_on_checkout',
+                 'When patron A checks out item on hold for patron B, ' ||
+                'automatically clear the hold for patron B. This is ' ||
+                 'desirable when checking out item for family members',
+               'coust', 'description'),
+       'bool');
+
+INSERT INTO actor.org_unit_setting (
+    org_unit, name, value
+) VALUES (
+    (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
+    'circ.clear_hold_on_checkout',
+    'false'
+);
+
+INSERT INTO config.org_unit_setting_type
+( name, grp, label, description, datatype )
+VALUES
 ('circ.patron_search.diacritic_insensitive',
  'circ',
  oils_i18n_gettext('circ.patron_search.diacritic_insensitive',
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.hold_checkout.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.hold_checkout.sql
new file mode 100644 (file)
index 0000000..1541b3b
--- /dev/null
@@ -0,0 +1,29 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+( name, grp, label, description, datatype )
+VALUES
+       ('circ.clear_hold_on_checkout',
+        'circ',
+       oils_i18n_gettext('circ.clear_hold_on_checkout',
+               'Clear hold when other patron checks out item',
+               'coust', 'label'),
+        oils_i18n_gettext('circ.clear_hold_on_checkout',
+                 'When patron A checks out item on hold for patron B, ' ||
+                'automatically clear the hold for patron B. This is ' ||
+                 'desirable when checking out item for family members',
+               'coust', 'description'),
+       'bool');
+
+INSERT INTO actor.org_unit_setting (
+    org_unit, name, value
+) VALUES (
+    (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
+    'circ.clear_hold_on_checkout',
+    'false'
+);
+
+COMMIT;
+
index 7308145..0adeb11 100644 (file)
         <div class="panel-body">
           <div ng-if="copy_barcode" class="strong-text-2">{{copy_barcode}}</div>
           {{evt.desc}}
+          <div ng-if="evt.textcode == 'ITEM_ON_HOLDS_SHELF'">
+             [% l('for ') %] {{patronName}}.
+            <div>
+               <label><input type="checkbox" ng-model="formdata.clearHold"/> 
+               [% l('Cancel this hold upon checkout?') %]</label>
+           </div>
+
+         </div>
         </div>
       </div>
     </div>
index f749b92..3bf866c 100644 (file)
@@ -27,9 +27,12 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
             'ui.staff.require_initials.patron_standing_penalty',
             'ui.admin.work_log.max_entries',
             'ui.admin.patron_log.max_entries',
-            'circ.staff_client.do_not_auto_attempt_print'
+            'circ.staff_client.do_not_auto_attempt_print',
+            'circ.clear_hold_on_checkout'
         ]).then(function(set) {
             service.require_initials = Boolean(set['ui.staff.require_initials.patron_standing_penalty']);
+           service.clearHold = Boolean(set['circ.clear_hold_on_checkout']);
+
             if (angular.isArray(set['circ.staff_client.do_not_auto_attempt_print'])) {
                 if (set['circ.staff_client.do_not_auto_attempt_print'].indexOf('Hold Slip') > 1)
                     service.never_auto_print['hold_shelf_slip'] = true;
@@ -732,12 +735,53 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
                 ['$scope', '$uibModalInstance', 
                 function($scope, $uibModalInstance) {
                 $scope.events = evt;
+
+                // Find the event, if any, that is for ITEM_ON_HOLDS_SHELF
+                //  and grab the patron name of the owner. 
+                $scope.holdEvent = evt.filter(
+                    function(e) {
+                        return e.textcode === 'ITEM_ON_HOLDS_SHELF'
+                     }
+                );
+
+                if ($scope.holdEvent) {
+                  // Ensure we have a scalar here 
+                   if (angular.isArray($scope.holdEvent)) {
+                       $scope.holdEvent = $scope.holdEvent[0];
+                   }
+
+                   $scope.patronName = $scope.holdEvent.payload.patron_name;
+                   $scope.holdID = $scope.holdEvent.payload.hold_id;
+                }
+
                 $scope.auto_override =
                     evt.filter(function(e){
                         return service.checkout_auto_override_after_first.indexOf(evt.textcode) > -1;
                     }).length > 0;
                 $scope.copy_barcode = params.copy_barcode; // may be null
-                $scope.ok = function() { $uibModalInstance.close() }
+
+                // Implementation note: Why not use a primitive here? It
+                // doesn't work.  See: 
+                // http://stackoverflow.com/questions/18642371/checkbox-not-binding-to-scope-in-angularjs
+                $scope.formdata = {clearHold : service.clearHold};
+
+                $scope.ok = function() { 
+                        $uibModalInstance.close();
+
+                        // Handle the cancellation of the assciated hold here
+                        if ($scope.formdata.clearHold && $scope.holdID) {
+                            $scope.args = {
+                            cancel_reason : 5,
+                                note: 'Item checked out by other patron'
+                           };
+                            egCore.net.request(
+                                'open-ils.circ', 'open-ils.circ.hold.cancel',
+                                egCore.auth.token(), $scope.holdID,
+                                $scope.args.cancel_reason,
+                                $scope.args.note);
+                        }
+               }
+
                 $scope.cancel = function ($event) { 
                     $uibModalInstance.dismiss();
                     $event.preventDefault();
diff --git a/docs/RELEASE_NOTES_NEXT/Circulation/alternate_patron_hold_pickup.adoc b/docs/RELEASE_NOTES_NEXT/Circulation/alternate_patron_hold_pickup.adoc
new file mode 100644 (file)
index 0000000..d3ef60e
--- /dev/null
@@ -0,0 +1,20 @@
+Alternate Patron Hold Pickup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This feature adds a bit of convenience to a common task: checking out
+an item on hold to another patron (typically a family member or helper).
+
+When you checkout the item, you will get a pop-up window with warnings associated
+with this item.  The "ITEM_ON_HOLDS_SHELF" message is now expanded to
+
+ * Let you know the name of the person who had placed the hold.
+ * Give you the option (in the form of a checkbox) of cancelling the
+   hold placed by the above-named patron.  (Checked = Cancel the hold;
+   Uncheked = Leave the hold in place)
+
+The initial value of the checkbox is derived from the circ.clear_hold_on_checkout
+organizational setting.  This is found under Administration -- Server Administration -- Org Unit Setting Types
+
+If the operator has CANCEL_HOLD privilege, then if the checkbox is checked and the checkout is allowed to proceed,
+the hold will be cancelled with a note that the item was checked out to another patrron.
+
+This feature is available in the browser-based staff client.