1 # ---------------------------------------------------------------
2 # Copyright (C) 2005 Georgia Public Library Service
3 # Bill Erickson <highfalutin@gmail.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
17 package OpenILS::Application::Circ::Holds;
18 use base qw/OpenSRF::Application/;
19 use strict; use warnings;
20 use OpenILS::Application::AppUtils;
22 use OpenSRF::EX qw(:try);
25 use OpenSRF::Utils::Logger qw(:logger);
26 use OpenILS::Utils::Editor q/:funcs/;
27 use OpenILS::Utils::PermitHold;
29 my $apputils = "OpenILS::Application::AppUtils";
34 __PACKAGE__->register_method(
35 method => "create_hold",
36 api_name => "open-ils.circ.holds.create",
38 Create a new hold for an item. From a permissions perspective,
39 the login session is used as the 'requestor' of the hold.
40 The hold recipient is determined by the 'usr' setting within
43 First we verify the requestion has holds request permissions.
44 Then we verify that the recipient is allowed to make the given hold.
45 If not, we see if the requestor has "override" capabilities. If not,
46 a permission exception is returned. If permissions allow, we cycle
47 through the set of holds objects and create.
49 If the recipient does not have permission to place multiple holds
50 on a single title and said operation is attempted, a permission
55 __PACKAGE__->register_method(
56 method => "create_hold",
57 api_name => "open-ils.circ.holds.create.override",
59 If the recipient is not allowed to receive the requested hold,
60 call this method to attempt the override
61 @see open-ils.circ.holds.create
66 my( $self, $conn, $auth, @holds ) = @_;
67 my $e = new_editor(authtoken=>$auth);
68 return $e->event unless $e->checkauth;
70 my $holds = (ref($holds[0] eq 'ARRAY')) ? $holds[0] : [@holds];
72 for my $hold (@$holds) {
76 my $requestor = $e->requestor;
77 my $recipient = $requestor;
80 if( $requestor->id ne $hold->usr ) {
81 # Make sure the requestor is allowed to place holds for
82 # the recipient if they are not the same people
83 $recipient = $e->retrieve_actor_user($hold->usr) or return $e->event;
84 $e->allowed('REQUEST_HOLDS', $recipient->home_ou) or return $e->event;
88 # Now make sure the recipient is allowed to receive the specified hold
90 my $porg = $recipient->home_ou;
91 my $pid = $recipient->id;
92 my $t = $hold->hold_type;
94 # See if a duplicate hold already exists
96 usr => $recipient->id,
98 fulfillment_time => undef,
99 target => $hold->target
102 $sargs->{holdable_formats} = $hold->holdable_formats if $t eq 'M';
104 my $existing = $e->search_action_hold_request($sargs);
105 my $eevt = OpenILS::Event->new('HOLD_EXISTS') if @$existing;
107 if( $t eq 'M' ) { $pevt = $e->event unless $e->checkperm($pid, $porg, 'MR_HOLDS'); }
108 if( $t eq 'T' ) { $pevt = $e->event unless $e->checkperm($pid, $porg, 'TITLE_HOLDS'); }
109 if( $t eq 'V' ) { $pevt = $e->event unless $e->checkperm($pid, $porg, 'VOLUME_HOLDS'); }
110 if( $t eq 'C' ) { $pevt = $e->event unless $e->checkperm($pid, $porg, 'COPY_HOLDS'); }
113 if( $self->api_name =~ /override/ ) {
114 # The recipient is not allowed to receive the requested hold
115 # and the requestor has elected to override -
116 # let's see if the requestor is allowed
117 return $e->event unless $e->allowed('REQUEST_HOLDS_OVERRIDE', $porg);
124 if( $self->api_name =~ /override/ ) {
125 return $e->event unless $e->allowed('CREATE_DUPLICATE_HOLDS', $porg);
132 $hold->requestor($e->requestor->id);
133 $hold->selection_ou($recipient->home_ou) unless $hold->selection_ou;
134 $e->create_action_hold_request($hold) or return $e->event;
141 my( $self, $client, $login_session, @holds) = @_;
143 if(!@holds){return 0;}
144 my( $user, $evt ) = $apputils->checkses($login_session);
148 if(ref($holds[0]) eq 'ARRAY') {
150 } else { $holds = [ @holds ]; }
152 $logger->debug("Iterating over holds requests...");
154 for my $hold (@$holds) {
157 my $type = $hold->hold_type;
159 $logger->activity("User " . $user->id .
160 " creating new hold of type $type for user " . $hold->usr);
163 if($user->id ne $hold->usr) {
164 ( $recipient, $evt ) = $apputils->fetch_user($hold->usr);
174 # am I allowed to place holds for this user?
175 if($hold->requestor ne $hold->usr) {
176 $perm = _check_request_holds_perm($user->id, $user->home_ou);
177 if($perm) { return $perm; }
180 # is this user allowed to have holds of this type?
181 $perm = _check_holds_perm($type, $hold->usr, $recipient->home_ou);
183 #if there is a requestor, see if the requestor has override privelages
184 if($hold->requestor ne $hold->usr) {
185 $perm = _check_request_holds_override($user->id, $user->home_ou);
186 if($perm) {return $perm;}
193 #enforce the fact that the login is the one requesting the hold
194 $hold->requestor($user->id);
195 $hold->selection_ou($recipient->home_ou) unless $hold->selection_ou;
197 my $resp = $apputils->simplereq(
199 'open-ils.storage.direct.action.hold_request.create', $hold );
202 return OpenSRF::EX::ERROR ("Error creating hold");
209 # makes sure that a user has permission to place the type of requested hold
210 # returns the Perm exception if not allowed, returns undef if all is well
211 sub _check_holds_perm {
212 my($type, $user_id, $org_id) = @_;
216 if($evt = $apputils->check_perms(
217 $user_id, $org_id, "MR_HOLDS")) {
221 } elsif ($type eq "T") {
222 if($evt = $apputils->check_perms(
223 $user_id, $org_id, "TITLE_HOLDS")) {
227 } elsif($type eq "V") {
228 if($evt = $apputils->check_perms(
229 $user_id, $org_id, "VOLUME_HOLDS")) {
233 } elsif($type eq "C") {
234 if($evt = $apputils->check_perms(
235 $user_id, $org_id, "COPY_HOLDS")) {
243 # tests if the given user is allowed to place holds on another's behalf
244 sub _check_request_holds_perm {
247 if(my $evt = $apputils->check_perms(
248 $user_id, $org_id, "REQUEST_HOLDS")) {
253 sub _check_request_holds_override {
256 if(my $evt = $apputils->check_perms(
257 $user_id, $org_id, "REQUEST_HOLDS_OVERRIDE")) {
262 __PACKAGE__->register_method(
263 method => "retrieve_holds_by_id",
264 api_name => "open-ils.circ.holds.retrieve_by_id",
266 Retrieve the hold, with hold transits attached, for the specified id
267 The login session is the requestor and if the requestor is
268 different from the user, then the requestor must have VIEW_HOLD permissions.
272 sub retrieve_holds_by_id {
273 my($self, $client, $login_session, $hold_id) = @_;
276 #my( $user, $target, $evt ) = $apputils->checkses_requestor(
277 # $login_session, $user_id, 'VIEW_HOLD' );
278 #return $evt if $evt;
280 my $holds = $apputils->simplereq(
282 "open-ils.storage.direct.action.hold_request.search.atomic",
283 "id" => $hold_id , fulfillment_time => undef, { order_by => "request_time" });
285 for my $hold ( @$holds ) {
287 $apputils->simplereq(
289 "open-ils.storage.direct.action.hold_transit_copy.search.hold.atomic" => $hold->id,
290 { order_by => 'id desc', limit => 1 }
299 __PACKAGE__->register_method(
300 method => "retrieve_holds",
301 api_name => "open-ils.circ.holds.retrieve",
303 Retrieves all the holds, with hold transits attached, for the specified
304 user id. The login session is the requestor and if the requestor is
305 different from the user, then the requestor must have VIEW_HOLD permissions.
310 my($self, $client, $login_session, $user_id) = @_;
312 my( $user, $target, $evt ) = $apputils->checkses_requestor(
313 $login_session, $user_id, 'VIEW_HOLD' );
316 my $holds = $apputils->simplereq(
318 "open-ils.storage.direct.action.hold_request.search.atomic",
319 "usr" => $user_id , fulfillment_time => undef, { order_by => "request_time" });
321 for my $hold ( @$holds ) {
323 $apputils->simplereq(
325 "open-ils.storage.direct.action.hold_transit_copy.search.hold.atomic" => $hold->id,
326 { order_by => 'id desc', limit => 1 }
334 __PACKAGE__->register_method(
335 method => "retrieve_holds_by_pickup_lib",
336 api_name => "open-ils.circ.holds.retrieve_by_pickup_lib",
338 Retrieves all the holds, with hold transits attached, for the specified
343 sub retrieve_holds_by_pickup_lib {
344 my($self, $client, $login_session, $ou_id) = @_;
346 #FIXME -- put an appropriate permission check here
347 #my( $user, $target, $evt ) = $apputils->checkses_requestor(
348 # $login_session, $user_id, 'VIEW_HOLD' );
349 #return $evt if $evt;
351 my $holds = $apputils->simplereq(
353 "open-ils.storage.direct.action.hold_request.search.atomic",
354 "pickup_lib" => $ou_id , fulfillment_time => undef, { order_by => "request_time" });
356 for my $hold ( @$holds ) {
358 $apputils->simplereq(
360 "open-ils.storage.direct.action.hold_transit_copy.search.hold.atomic" => $hold->id,
361 { order_by => 'id desc', limit => 1 }
370 __PACKAGE__->register_method(
371 method => "cancel_hold",
372 api_name => "open-ils.circ.hold.cancel",
374 Cancels the specified hold. The login session
375 is the requestor and if the requestor is different from the usr field
376 on the hold, the requestor must have CANCEL_HOLDS permissions.
377 the hold may be either the hold object or the hold id
381 my($self, $client, $login_session, $holdid) = @_;
384 my($user, $evt) = $U->checkses($login_session);
387 ( $hold, $evt ) = $U->fetch_hold($holdid);
390 if($user->id ne $hold->usr) { #am I allowed to cancel this user's hold?
391 if($evt = $apputils->check_perms(
392 $user->id, $user->home_ou, 'CANCEL_HOLDS')) {
397 $logger->activity( "User " . $user->id .
398 " canceling hold $holdid for user " . $hold->usr );
400 return $apputils->simplereq(
402 "open-ils.storage.direct.action.hold_request.delete", $hold );
406 __PACKAGE__->register_method(
407 method => "update_hold",
408 api_name => "open-ils.circ.hold.update",
410 Updates the specified hold. The login session
411 is the requestor and if the requestor is different from the usr field
412 on the hold, the requestor must have UPDATE_HOLDS permissions.
416 my($self, $client, $login_session, $hold) = @_;
418 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
419 $login_session, $hold->usr, 'UPDATE_HOLD' );
422 $logger->activity('User ' . $requestor->id .
423 ' updating hold ' . $hold->id . ' for user ' . $target->id );
425 return $U->storagereq(
426 "open-ils.storage.direct.action.hold_request.update", $hold );
430 __PACKAGE__->register_method(
431 method => "retrieve_hold_status",
432 api_name => "open-ils.circ.hold.status.retrieve",
434 Calculates the current status of the hold.
435 the requestor must have VIEW_HOLD permissions if the hold is for a user
436 other than the requestor.
437 Returns -1 on error (for now)
438 Returns 1 for 'waiting for copy to become available'
439 Returns 2 for 'waiting for copy capture'
440 Returns 3 for 'in transit'
441 Returns 4 for 'arrived'
444 sub retrieve_hold_status {
445 my($self, $client, $login_session, $hold_id) = @_;
448 my( $requestor, $target, $hold, $copy, $transit, $evt );
450 ( $hold, $evt ) = $apputils->fetch_hold($hold_id);
453 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
454 $login_session, $hold->usr, 'VIEW_HOLD' );
457 return 1 unless (defined($hold->current_copy));
459 ( $copy, $evt ) = $apputils->fetch_copy($hold->current_copy);
462 return 4 if ($hold->capture_time and $copy->circ_lib eq $hold->pickup_lib);
464 ( $transit, $evt ) = $apputils->fetch_hold_transit_by_hold( $hold->id );
465 return 4 if(ref($transit) and defined($transit->dest_recv_time) );
467 return 3 if defined($hold->capture_time);
476 __PACKAGE__->register_method(
477 method => "capture_copy",
478 api_name => "open-ils.circ.hold.capture_copy.barcode",
480 Captures a copy to fulfil a hold
481 Params is login session and copy barcode
482 Optional param is 'flesh'. If set, we also return the
483 relevant copy and title
484 login mus have COPY_CHECKIN permissions (since this is essentially
488 # XXX deprecate me XXX
491 my( $self, $client, $login_session, $params ) = @_;
492 my %params = %$params;
493 my $barcode = $params{barcode};
496 my( $user, $target, $copy, $hold, $evt );
498 ( $user, $evt ) = $apputils->checkses($login_session);
501 # am I allowed to checkin a copy?
502 $evt = $apputils->check_perms($user->id, $user->home_ou, "COPY_CHECKIN");
505 $logger->info("Capturing copy with barcode $barcode");
507 my $session = $apputils->start_db_session();
509 ($copy, $evt) = $apputils->fetch_copy_by_barcode($barcode);
512 $logger->debug("Capturing copy " . $copy->id);
514 ( $hold, $evt ) = _find_local_hold_for_copy($session, $copy, $user);
517 warn "Found hold " . $hold->id . "\n";
518 $logger->info("We found a hold " .$hold->id. "for capturing copy with barcode $barcode");
520 $hold->current_copy($copy->id);
521 $hold->capture_time("now");
524 my $stat = $session->request(
525 "open-ils.storage.direct.action.hold_request.update", $hold)->gather(1);
526 if(!$stat) { throw OpenSRF::EX::ERROR
527 ("Error updating hold request " . $copy->id); }
529 $copy->status(8); #status on holds shelf
531 # if the staff member capturing this item is not at the pickup lib
532 if( $user->home_ou ne $hold->pickup_lib ) {
533 $self->_build_hold_transit( $login_session, $session, $hold, $user, $copy );
536 $copy->editor($user->id);
537 $copy->edit_date("now");
538 $stat = $session->request(
539 "open-ils.storage.direct.asset.copy.update", $copy )->gather(1);
540 if(!$stat) { throw OpenSRF::EX ("Error updating copy " . $copy->id); }
542 my $payload = { hold => $hold };
543 $payload->{copy} = $copy if $params{flesh_copy};
545 if($params{flesh_record}) {
547 ($record, $evt) = $apputils->fetch_record_by_copy( $copy->id );
549 $record = $apputils->record_to_mvr($record);
550 $payload->{record} = $record;
553 $apputils->commit_db_session($session);
555 return OpenILS::Event->new('ROUTE_ITEM',
556 route_to => $hold->pickup_lib, payload => $payload );
559 sub _build_hold_transit {
560 my( $self, $login_session, $session, $hold, $user, $copy ) = @_;
561 my $trans = Fieldmapper::action::hold_transit_copy->new;
563 $trans->hold($hold->id);
564 $trans->source($user->home_ou);
565 $trans->dest($hold->pickup_lib);
566 $trans->source_send_time("now");
567 $trans->target_copy($copy->id);
568 $trans->copy_status($copy->status);
570 my $meth = $self->method_lookup("open-ils.circ.hold_transit.create");
571 my ($stat) = $meth->run( $login_session, $trans, $session );
572 if(!$stat) { throw OpenSRF::EX ("Error creating new hold transit"); }
573 else { $copy->status(6); } #status in transit
577 sub find_local_hold {
578 my( $class, $session, $copy, $user ) = @_;
579 return _find_local_hold_for_copy($session, $copy, $user);
582 sub _find_local_hold_for_copy {
587 my $evt = OpenILS::Event->new('ACTION_HOLD_REQUEST_NOT_FOUND');
589 # first see if this copy has already been selected to fulfill a hold
590 my $hold = $session->request(
591 "open-ils.storage.direct.action.hold_request.search_where",
592 { current_copy => $copy->id, capture_time => undef } )->gather(1);
594 if($hold) {return $hold;}
596 $logger->debug("searching for local hold at org " .
597 $user->home_ou . " and copy " . $copy->id);
599 my $holdid = $session->request(
600 "open-ils.storage.action.hold_request.nearest_hold",
601 $user->home_ou, $copy->id )->gather(1);
603 return (undef, $evt) unless defined $holdid;
605 $logger->debug("Found hold id $holdid while ".
606 "searching nearest hold to " .$user->home_ou);
608 return $apputils->fetch_hold($holdid);
612 __PACKAGE__->register_method(
613 method => "create_hold_transit",
614 api_name => "open-ils.circ.hold_transit.create",
616 Creates a new transit object
619 sub create_hold_transit {
620 my( $self, $client, $login_session, $transit, $session ) = @_;
622 my( $user, $evt ) = $apputils->checkses($login_session);
624 $evt = $apputils->check_perms($user->id, $user->home_ou, "CREATE_TRANSIT");
628 if($session) { $ses = $session; }
629 else { $ses = OpenSRF::AppSession->create("open-ils.storage"); }
631 return $ses->request(
632 "open-ils.storage.direct.action.hold_transit_copy.create", $transit )->gather(1);
636 sub fetch_open_hold_by_current_copy {
639 my $hold = $apputils->simplereq(
641 'open-ils.storage.direct.action.hold_request.search.atomic',
642 current_copy => $copyid , fulfillment_time => undef );
643 return $hold->[0] if ref($hold);
647 sub fetch_related_holds {
650 return $apputils->simplereq(
652 'open-ils.storage.direct.action.hold_request.search.atomic',
653 current_copy => $copyid , fulfillment_time => undef );
657 __PACKAGE__->register_method (
658 method => "hold_pull_list",
659 api_name => "open-ils.circ.hold_pull_list.retrieve",
661 Returns a list of hold ID's that need to be "pulled"
667 my( $self, $conn, $authtoken, $limit, $offset ) = @_;
668 my( $reqr, $evt ) = $U->checkses($authtoken);
671 my $org = $reqr->ws_ou || $reqr->home_ou;
672 # the perm locaiton shouldn't really matter here since holds
673 # will exist all over and VIEW_HOLDS should be universal
674 $evt = $U->check_perms($reqr->id, $org, 'VIEW_HOLD');
677 return $U->storagereq(
678 'open-ils.storage.direct.action.hold_request.pull_list.search.current_copy_circ_lib.atomic',
679 $org, $limit, $offset ); # XXX change to workstation
682 __PACKAGE__->register_method (
683 method => 'fetch_hold_notify',
684 api_name => 'open-ils.circ.hold_notification.retrieve_by_hold',
686 Returns a list of hold notification objects based on hold id.
687 @param authtoken The loggin session key
688 @param holdid The id of the hold whose notifications we want to retrieve
689 @return An array of hold notification objects, event on error.
693 sub fetch_hold_notify {
694 my( $self, $conn, $authtoken, $holdid ) = @_;
695 my( $requestor, $evt ) = $U->checkses($authtoken);
698 ($hold, $evt) = $U->fetch_hold($holdid);
700 ($patron, $evt) = $U->fetch_user($hold->usr);
703 $evt = $U->check_perms($requestor->id, $patron->home_ou, 'VIEW_HOLD_NOTIFICATION');
706 $logger->info("User ".$requestor->id." fetching hold notifications for hold $holdid");
707 return $U->storagereq(
708 'open-ils.storage.direct.action.hold_notification.search.hold.atomic', $holdid );
712 __PACKAGE__->register_method (
713 method => 'create_hold_notify',
714 api_name => 'open-ils.circ.hold_notification.create',
716 Creates a new hold notification object
717 @param authtoken The login session key
718 @param notification The hold notification object to create
719 @return ID of the new object on success, Event on error
722 sub create_hold_notify {
723 my( $self, $conn, $authtoken, $notification ) = @_;
724 my( $requestor, $evt ) = $U->checkses($authtoken);
727 ($hold, $evt) = $U->fetch_hold($notification->hold);
729 ($patron, $evt) = $U->fetch_user($hold->usr);
732 # XXX perm depth probably doesn't matter here -- should always be consortium level
733 $evt = $U->check_perms($requestor->id, $patron->home_ou, 'CREATE_HOLD_NOTIFICATION');
736 # Set the proper notifier
737 $notification->notify_staff($requestor->id);
738 my $id = $U->storagereq(
739 'open-ils.storage.direct.action.hold_notification.create', $notification );
740 return $U->DB_UPDATE_FAILED($notification) unless $id;
741 $logger->info("User ".$requestor->id." successfully created new hold notification $id");
746 __PACKAGE__->register_method(
747 method => 'reset_hold',
748 api_name => 'open-ils.circ.hold.reset',
750 Un-captures and un-targets a hold, essentially returning
751 it to the state it was in directly after it was placed,
752 then attempts to re-target the hold
753 @param authtoken The login session key
754 @param holdid The id of the hold
760 my( $self, $conn, $auth, $holdid ) = @_;
762 my ($hold, $evt) = $U->fetch_hold($holdid);
764 ($reqr, $evt) = $U->checksesperm($auth, 'UPDATE_HOLD');
766 $evt = $self->_reset_hold($reqr, $hold);
772 my ($self, $reqr, $hold, $session) = @_;
777 $session = $U->start_db_session();
780 $hold->clear_capture_time;
781 $hold->clear_current_copy;
783 return $U->DB_UPDATE_FAILED($hold) unless
785 'open-ils.storage.direct.action.hold_request.update', $hold )->gather(1);
788 'open-ils.storage.action.hold_request.copy_targeter', undef, $hold->id )->gather(1);
790 $U->commit_db_session($session) unless $x;
795 __PACKAGE__->register_method(
796 method => 'fetch_open_title_holds',
797 api_name => 'open-ils.circ.open_holds.retrieve',
799 Returns a list ids of un-fulfilled holds for a given title id
800 @param authtoken The login session key
801 @param id the id of the item whose holds we want to retrieve
802 @param type The hold type - M, T, V, C
806 sub fetch_open_title_holds {
807 my( $self, $conn, $auth, $id, $type, $org ) = @_;
808 my $e = OpenILS::Utils::Editor->new( authtoken => $auth );
809 return $e->event unless $e->checkauth;
812 $org ||= $e->requestor->ws_ou;
814 # return $e->search_action_hold_request(
815 # { target => $id, hold_type => $type, fulfillment_time => undef }, {idlist=>1});
817 # XXX make me return IDs in the future ^--
818 return $e->search_action_hold_request(
819 { target => $id, hold_type => $type, fulfillment_time => undef });
825 __PACKAGE__->register_method(
826 method => 'fetch_captured_holds',
827 api_name => 'open-ils.circ.captured_holds.on_shelf.retrieve',
829 Returns a list ids of un-fulfilled holds for a given title id
830 @param authtoken The login session key
831 @param org The org id of the location in question
834 sub fetch_captured_holds {
835 my( $self, $conn, $auth, $org ) = @_;
837 my $e = OpenILS::Utils::Editor->new(authtoken => $auth);
838 return $e->event unless $e->checkauth;
839 return $e->event unless $e->allowed('VIEW_HOLD'); # XXX rely on editor perm
841 $org ||= $e->requestor->ws_ou;
843 my $holds = $e->search_action_hold_request(
845 capture_time => { "!=" => undef },
846 current_copy => { "!=" => undef },
847 fulfillment_time => undef,
853 my $stat = $U->copy_status_from_name('on holds shelf');
854 for my $h (@$holds) {
855 my $copy = $e->retrieve_asset_copy($h->current_copy)
857 push( @res, $h ) if $copy->status == $stat->id; # eventually, push IDs here
867 __PACKAGE__->register_method(
868 method => "check_title_hold",
869 api_name => "open-ils.circ.title_hold.is_possible",
871 Determines if a hold were to be placed by a given user,
872 whether or not said hold would have any potential copies
874 @param authtoken The login session key
875 @param params A hash of named params including:
876 patronid - the id of the hold recipient
877 titleid (brn) - the id of the title to be held
878 depth - the hold range depth (defaults to 0)
881 sub check_title_hold {
882 my( $self, $client, $authtoken, $params ) = @_;
884 my %params = %$params;
885 my $titleid = $params{titleid} ||"";
886 my $mrid = $params{mrid} ||"";
887 my $depth = $params{depth} || 0;
888 my $pickup_lib = $params{pickup_lib};
889 my $hold_type = $params{hold_type} || 'T';
891 my $e = new_editor(authtoken=>$authtoken);
892 return $e->event unless $e->checkauth;
893 my $patron = $e->retrieve_actor_user($params{patronid})
895 return $e->event unless $e->allowed('VIEW_HOLD_PERMIT', $patron->home_ou);
897 my $rangelib = $params{range_lib} || $patron->home_ou;
899 my $request_lib = $e->retrieve_actor_org_unit($e->requestor->ws_ou)
902 if( $hold_type eq 'T' ) {
903 return _check_title_hold_is_possible(
904 $titleid, $rangelib, $depth, $request_lib, $patron, $e->requestor, $pickup_lib);
907 if( $hold_type eq 'M' ) {
908 my $maps = $e->search_metabib_source_map({metarecord=>$mrid});
909 my @recs = map { $_->source } @$maps;
910 for my $rec (@recs) {
911 return 1 if (_check_title_hold_is_possible(
912 $rec, $rangelib, $depth, $request_lib, $patron, $e->requestor, $pickup_lib));
919 sub _check_title_hold_is_possible {
920 my( $titleid, $rangelib, $depth, $request_lib, $patron, $requestor, $pickup_lib ) = @_;
926 $logger->debug("Fetching ranged title tree for title $titleid, org $rangelib, depth $depth");
928 while( $title = $U->storagereq(
929 'open-ils.storage.biblio.record_entry.ranged_tree',
930 $titleid, $rangelib, $depth, $limit, $offset ) ) {
934 ref($title->call_numbers) and
935 @{$title->call_numbers};
937 for my $cn (@{$title->call_numbers}) {
939 $logger->debug("Checking callnumber ".$cn->id." for hold fulfillment possibility");
941 for my $copy (@{$cn->copies}) {
943 $logger->debug("Checking copy ".$copy->id." for hold fulfillment possibility");
945 return 1 if OpenILS::Utils::PermitHold::permit_copy_hold(
947 requestor => $requestor,
950 title_descriptor => $title->fixed_fields, # this is fleshed into the title object
951 pickup_lib => $pickup_lib,
952 request_lib => $request_lib
956 $logger->debug("Copy ".$copy->id." for hold fulfillment possibility failed...");