$self->fake_hold_dest(0);
return if $self->bail_out;
+ } elsif ($hold and $hold->hold_type eq 'R') {
+
+ $self->copy->status(OILS_COPY_STATUS_CATALOGING);
+ $self->notify_hold(0); # No need to notify
+ $self->fake_hold_dest(0);
+ $self->noop(1); # Don't try and capture for other holds/transits now
+ $self->update_copy();
+ $hold->fulfillment_time('now');
+ $self->bail_on_events($self->editor->event)
+ unless $self->editor->update_action_hold_request($hold);
+
} else {
# hold transited to correct location
$self->retarget($retarget);
+ my $suppress_transit = 0;
+ if( $hold->pickup_lib != $self->circ_lib and not $self->hold_as_transit ) {
+ my $suppress_transit_circ = $U->ou_ancestor_setting($self->circ_lib, 'circ.transit.suppress_hold');
+ if($suppress_transit_circ && $suppress_transit_circ->{value}) {
+ my $suppress_transit_pickup = $U->ou_ancestor_setting($hold->pickup_lib, 'circ.transit.suppress_hold');
+ if($suppress_transit_pickup && $suppress_transit_circ->{value} eq $suppress_transit_pickup->{value}) {
+ $suppress_transit = 1;
+ $self->hold->pickup_lib($self->circ_lib);
+ }
+ }
+ }
+
$logger->info("circulator: found permitted hold ".$hold->id." for copy, capturing...");
$hold->current_copy($copy->id);
$hold->capture_time('now');
$self->put_hold_on_shelf($hold)
- if $hold->pickup_lib == $self->circ_lib;
+ if ($suppress_transit || ($hold->pickup_lib == $self->circ_lib and not $self->hold_as_transit) );
# prevent DB errors caused by fetching
# holds from storage, and updating through cstore
return 0 if $self->bail_out;
- my $suppress_transit = 0;
- if( $hold->pickup_lib != $self->circ_lib and not $self->hold_as_transit ) {
- my $suppress_transit_circ = $U->ou_ancestor_setting($self->circ_lib, 'circ.transit.suppress_hold');
- if($suppress_transit_circ && $suppress_transit_circ->{value}) {
- my $suppress_transit_pickup = $U->ou_ancestor_setting($hold->pickup_lib, 'circ.transit.suppress_hold');
- if($suppress_transit_pickup && $suppress_transit_circ->{value} eq $suppress_transit_pickup->{value}) {
- $suppress_transit = 1;
- $self->hold->pickup_lib($self->circ_lib);
- }
- }
- }
-
if( $suppress_transit or ( $hold->pickup_lib == $self->circ_lib && not $self->hold_as_transit ) ) {
- # This hold was captured in the correct location
- $copy->status(OILS_COPY_STATUS_ON_HOLDS_SHELF);
- $self->push_events(OpenILS::Event->new('SUCCESS'));
+ if ($hold->hold_type eq 'R') {
+ $copy->status(OILS_COPY_STATUS_CATALOGING);
+ $hold->fulfillment_time('now');
+ $self->noop(1); # Block other transit/hold checks
+ $self->bail_on_events($self->editor->event)
+ unless $self->editor->update_action_hold_request($hold);
+ } else {
+ # This hold was captured in the correct location
+ $copy->status(OILS_COPY_STATUS_ON_HOLDS_SHELF);
+ $self->push_events(OpenILS::Event->new('SUCCESS'));
- #$self->do_hold_notify($hold->id);
- $self->notify_hold($hold->id);
+ #$self->do_hold_notify($hold->id);
+ $self->notify_hold($hold->id);
+ }
} else {
# make sure we save the copy status
$self->update_copy;
+ return 0 if $copy->status == OILS_COPY_STATUS_CATALOGING;
return 1;
}
return $e->die_event unless $e->allowed('ISSUANCE_HOLDS', $porg);
} elsif ( $t eq OILS_HOLD_TYPE_COPY ) {
return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
- } elsif ( $t eq OILS_HOLD_TYPE_FORCE ) {
- return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
- } elsif ( $t eq OILS_HOLD_TYPE_RECALL ) {
- return $e->die_event unless $e->allowed('COPY_HOLDS', $porg);
+ } elsif ( $t eq OILS_HOLD_TYPE_FORCE || $t eq OILS_HOLD_TYPE_RECALL ) {
+ my $copy = $e->retrieve_asset_copy($hold->target)
+ or return $e->die_event;
+ if ( $t eq OILS_HOLD_TYPE_FORCE ) {
+ return $e->die_event unless $e->allowed('COPY_HOLDS_FORCE', $copy->circ_lib);
+ } elsif ( $t eq OILS_HOLD_TYPE_RECALL ) {
+ return $e->die_event unless $e->allowed('COPY_HOLDS_RECALL', $copy->circ_lib);
+ }
}
if( @events ) {
return $e->event unless $volume = $e->retrieve_asset_call_number($copy->call_number);
return $e->event unless $title = $e->retrieve_biblio_record_entry($volume->record);
+ return (1, 1, []) if( $hold_type eq OILS_HOLD_TYPE_RECALL || $hold_type eq OILS_HOLD_TYPE_FORCE);
return verify_copy_for_hold(
$patron, $e->requestor, $title, $copy, $pickup_lib, $request_lib
);
}
);
- # hold->type "R" means we need this copy
- for my $h (@$old_holds) { return ($h) if $h->hold_type eq 'R'; }
-
-
my $hold_stall_interval = $U->ou_ancestor_setting_value($user->ws_ou, OILS_SETTING_HOLD_SOFT_STALL);
$logger->info("circulator: searching for best hold at org ".$user->ws_ou.
limit => 1
};
- if($hold_type eq 'C') {
+ if($hold_type eq 'C' or $hold_type eq 'F' or $hold_type eq 'R') {
$query->{where}->{'+acp'}->{id} = $hold_target;
AND h.cancel_time IS NULL
AND (h.expire_time IS NULL OR h.expire_time > NOW())
AND h.frozen IS FALSE
- ORDER BY $holdsort
+ ORDER BY CASE WHEN h.hold_type IN ('R','F') THEN 0 ELSE 1 END, $holdsort
LIMIT $limit
SQL
frozen => 'f',
prev_check_time => { '<=' => $expire_threshold },
},
- { order_by => 'CASE WHEN hold_type = \'F\' THEN 0 ELSE 1 END, selection_depth DESC, request_time,prev_check_time' } ) ];
+ { order_by => 'selection_depth DESC, request_time,prev_check_time' } ) ];
# find all the holds holds needing first time targeting
push @$holds, action::hold_request->search(
prev_check_time => undef,
frozen => 'f',
cancel_time => undef,
- { order_by => 'CASE WHEN hold_type = \'F\' THEN 0 ELSE 1 END, selection_depth DESC, request_time' } );
+ { order_by => 'selection_depth DESC, request_time' } );
} else {
# find all the holds holds needing first time targeting ONLY
prev_check_time => undef,
cancel_time => undef,
frozen => 'f',
- { order_by => 'CASE WHEN hold_type = \'F\' THEN 0 ELSE 1 END, selection_depth DESC, request_time' } ) ];
+ { order_by => 'selection_depth DESC, request_time' } ) ];
}
} catch Error with {
my $e = shift;
push @$all_copies, $_cp if $_cp;
}
- # trim unholdables
- @$all_copies = grep { isTrue($_->status->holdable) &&
- isTrue($_->location->holdable) &&
- isTrue($_->holdable) &&
- !isTrue($_->deleted) &&
- (isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1) &&
- ($hold->hold_type ne 'P' ? $_->part_maps->count == 0 : 1)
- } @$all_copies;
+ # Force and recall holds bypass pretty much everything
+ if ($hold->hold_type ne 'R' && $hold->hold_type ne 'F') {
+ # trim unholdables
+ @$all_copies = grep { isTrue($_->status->holdable) &&
+ isTrue($_->location->holdable) &&
+ isTrue($_->holdable) &&
+ !isTrue($_->deleted) &&
+ (isTrue($hold->mint_condition) ? isTrue($_->mint_condition) : 1) &&
+ ($hold->hold_type ne 'P' ? $_->part_maps->count == 0 : 1)
+ } @$all_copies;
+ }
# let 'em know we're still working
$client->status( new OpenSRF::DomainObject::oilsContinueStatus );
$all_copies = [grep { $_->status == 0 || $_->status == 7 } grep {''.$_->circ_lib ne $pu_lib } @good_copies];
# $all_copies is now a list of copies not at the pickup library
-
- my $best = choose_nearest_copy($hold, $prox_list);
+
+ my $best;
+ if ($hold->hold_type eq 'R' || $hold->hold_type eq 'F') { # Recall/Force holds bypass hold rules.
+ $best = $good_copies[0] if(scalar @good_copies);
+ } else {
+ $best = choose_nearest_copy($hold, $prox_list);
+ }
$client->status( new OpenSRF::DomainObject::oilsContinueStatus );
if (!$best) {
return __copy_to_title($e,
$e->retrieve_asset_copy($hold->target))
- if $hold->hold_type eq 'C';
+ if $hold->hold_type eq 'C' or $hold->hold_type eq 'F' or $hold->hold_type eq 'R';
return __volume_to_title($e,
$e->retrieve_asset_call_number($hold->target))
( 515, 'UPDATE_PATRON_PRIMARY_CARD', oils_i18n_gettext( 515,
'Allows a user to manually adjust a patron''s primary card', 'ppl', 'description')),
( 516, 'CREATE_REPORT_TEMPLATE', oils_i18n_gettext( 516,
- 'Allows a user to create report templates', 'ppl', 'description' ));
+ 'Allows a user to create report templates', 'ppl', 'description' )),
+ ( 517, 'COPY_HOLDS_FORCE', oils_i18n_gettext( 517,
+ 'Allow a user to place a force hold on a specific copy', 'ppl', 'description' )),
+ ( 518, 'COPY_HOLDS_RECALL', oils_i18n_gettext( 518,
+ 'Allow a user to place a recall hold on a specific copy', 'ppl', 'description' ));
SELECT SETVAL('permission.perm_list_id_seq'::TEXT, 1000);
--- /dev/null
+INSERT INTO permission.perm_list ( id, code, description ) VALUES
+ ( 517, 'COPY_HOLDS_FORCE', oils_i18n_gettext( 517,
+ 'Allow a user to place a force hold on a specific copy', 'ppl', 'description' )),
+ ( 518, 'COPY_HOLDS_RECALL', oils_i18n_gettext( 518,
+ 'Allow a user to place a recall hold on a specific copy', 'ppl', 'description' ));