When a new item is checked in local holds will be searched and re-targeted.
The search and retarget stops when a hold is found locally that the item can fill.
Already-captured holds will not be retargeted.
"New" is (currently) defined as "In progress" at checkin
Retarget occurs:
Only if the copy's circ library is the checkin library
Only if the checkin is an actual checkin (not a renewal)
Only if the checkin is capturing holds and transits
Signed-off-by: Thomas Berezansky <tsbere@mvlc.org>
Signed-off-by: Jason Etheridge <jason@esilibrary.com>
use_booking
generate_lost_overdue
clear_expired
+ retarget_mode
/;
if $horizon > DateTime->now;
}
+# Retarget local holds at checkin
+sub checkin_retarget {
+ my $self = shift;
+ return unless $self->retarget_mode =~ m/retarget/; # Retargeting?
+ return unless $self->is_checkin; # Renewals need not be checked
+ return if $self->capture eq 'nocapture'; # Not capturing holds anyway? Move on.
+ return if $self->is_precat; # No holds for precats
+ return unless $self->circ_lib == $self->copy->circ_lib; # Item isn't "home"? Don't check.
+ return unless $self->copy->holdable; # Not holdable, shouldn't capture holds.
+ # Specifically target items that are likely new (by status ID)
+ unless ($self->retarget_mode =~ m/\.all/) {
+ my $status = $U->copy_status($self->copy->status)->id;
+ return unless $status == OILS_COPY_STATUS_IN_PROCESS;
+ }
+
+ # Fetch holds for the bib
+ my ($result) = $holdcode->method_lookup('open-ils.circ.holds.retrieve_all_from_title')->run(
+ $self->editor->authtoken,
+ $self->title->id,
+ {
+ capture_time => undef, # No touching captured holds
+ frozen => 'f', # Don't bother with frozen holds
+ pickup_lib => $self->circ_lib # Only holds actually here
+ });
+
+ # Error? Skip the step.
+ return if exists $result->{"ilsevent"};
+
+ # Assemble holds
+ my $holds = [];
+ foreach my $holdlist (keys %{$result}) {
+ push @$holds, @{$result->{$holdlist}};
+ }
+
+ return if scalar(@$holds) == 0; # No holds, no retargeting
+
+ # Loop over holds in request-ish order
+ # Stage 1: Get them into request-ish order
+ # Also grab type and target for skipping low hanging ones
+ $result = $self->editor->json_query({
+ "select" => { "ahr" => ["id", "hold_type", "target"] },
+ "from" => { "ahr" => { "au" => { "fkey" => "usr", "join" => "pgt"} } },
+ "where" => { "id" => $holds },
+ "order_by" => [
+ { "class" => "pgt", "field" => "hold_priority"},
+ { "class" => "ahr", "field" => "cut_in_line", "direction" => "desc", "transform" => "coalesce", "params" => ['f']},
+ { "class" => "ahr", "field" => "selection_depth", "direction" => "desc"},
+ { "class" => "ahr", "field" => "request_time"}
+ ]
+ });
+
+ # Stage 2: Loop!
+ if (ref $result eq "ARRAY" and scalar @$result) {
+ foreach (@{$result}) {
+ # Copy level, but not this copy?
+ next if ($_->{hold_type} eq 'C' or $_->{hold_type} eq 'R' or $_->{hold_type} eq 'F'
+ and $_->{target} != $self->copy->id);
+ # Volume level, but not this volume?
+ next if ($_->{hold_type} eq 'V' and $_->{target} != $self->volume->id);
+ # So much for easy stuff, attempt a retarget!
+ my $tresult = $U->storagereq('open-ils.storage.action.hold_request.copy_targeter', undef, $_->{id}, $self->copy->id);
+ if(ref $tresult eq "ARRAY" and scalar @$tresult) {
+ last if(exists $tresult->[0]->{found_copy} and $tresult->[0]->{found_copy});
+ }
+ }
+ }
+}
sub do_checkin {
my $self = shift;
unless $self->copy;
$self->check_transit_checkin_interval;
+ $self->checkin_retarget;
# the renew code and mk_env should have already found our circulation object
unless( $self->circ ) {
my $client = shift;
my $check_expire = shift;
my $one_hold = shift;
+ my $find_copy = shift;
local $OpenILS::Application::Storage::WRITE = 1;
}
my $copy_count = @$all_copies;
+ my $found_copy = undef;
+ $found_copy = 1 if($find_copy and grep $_ == $find_copy, @$all_copies);
# map the potentials, so that we can pick up checkins
# XXX Loop-based targeting may require that /only/ copies from this loop should be added to
{ hold => $hold->id,
old_target => ($old_best ? $old_best->id : undef),
eligible_copies => $copy_count,
- target => ($best ? $best->id : undef) };
+ target => ($best ? $best->id : undef),
+ found_copy => $found_copy };
} otherwise {
my $e = shift;
<!ENTITY staff.circ.checkin_overlay.checkin_modifiers_btn.accesskey "M">
<!ENTITY staff.circ.checkin_overlay.checkin_clear_shelf_expired.label "Clear Shelf-Expired Holds">
<!ENTITY staff.circ.checkin_overlay.checkin_clear_shelf_expired.accesskey "C">
+<!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget.label "Retarget Local Holds">
+<!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget.accesskey "R">
+<!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget_all.label "Retarget All Statuses">
+<!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget_all.accesskey "e">
+<!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget_all_ind.label "Always Retarget Local Holds">
<!ENTITY staff.circ.renew_overlay.background_text "Renew Item">
<!ENTITY staff.circ.renew_overlay.sel_clip.label "Copy to Clipboard">
<!ENTITY staff.circ.renew_overlay.sel_clip.accesskey "C">
ind.hidden = cb.getAttribute('checked') != 'true';
document.getElementById('checkin_barcode_entry_textbox').focus();
return true;
+ } ],
+ 'cmd_checkin_auto_retarget' : [ ['command'], function(ev) {
+ dump('in cmd_checkin_auto_retarget\n');
+ var bg = document.getElementById('background');
+ var cb = document.getElementById('checkin_auto_retarget');
+ var cb2 = document.getElementById('checkin_auto_retarget_all');
+ var ind = document.getElementById('checkin_auto_retarget_indicator');
+ var ind2 = document.getElementById('checkin_auto_retarget_all_indicator');
+ var cn = 'checkin_screen_checkin_auto_retarget';
+ var cn2 = 'checkin_screen_checkin_auto_retarget_all';
+ if (cb.getAttribute('checked') == 'true') {
+ if(cb2.getAttribute('checked') == 'true') {
+ removeCSSClass(bg,cn);
+ addCSSClass(bg,cn2);
+ ind.hidden = true;
+ ind2.hidden = false;
+ } else {
+ addCSSClass(bg,cn);
+ removeCSSClass(bg,cn2);
+ ind.hidden = false;
+ ind2.hidden = true;
+ }
+ } else {
+ removeCSSClass(bg,cn);
+ removeCSSClass(bg,cn2);
+ ind.hidden = true;
+ ind2.hidden = true;
+ }
+ document.getElementById('checkin_barcode_entry_textbox').focus();
+ return true;
} ]
}
}
var clear_shelf_expired_holds = document.getElementById('checkin_clear_shelf_expired');
if (clear_shelf_expired_holds) clear_shelf_expired_holds = clear_shelf_expired_holds.getAttribute('checked') == 'true';
if (clear_shelf_expired_holds) params.clear_expired = 1;
+ var auto_retarget = document.getElementById('checkin_auto_retarget');
+ if (auto_retarget) auto_retarget = auto_retarget.getAttribute('checked') == 'true';
+ if (auto_retarget) {
+ var retarget_all = document.getElementById('checkin_auto_retarget_all');
+ if (retarget_all) retarget_all = retarget_all.getAttribute('checked') == 'true';
+ if (retarget_all) params.retarget_mode = 'retarget.all';
+ else params.retarget_mode = 'retarget';
+ }
circ.util.checkin_via_barcode(
ses(),
params,
<command id="cmd_amnesty_mode" />
<command id="cmd_checkin_auto_print_slips" />
<command id="cmd_checkin_clear_shelf_expired" />
+ <command id="cmd_checkin_auto_retarget" />
</commandset>
<description id="amnesty_mode_indicator" hidden="true">&staff.circ.checkin_overlay.amnesty_mode.label;</description>
<description id="checkin_auto_print_slips_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_auto_print_slips.label;</description>
<description id="checkin_clear_shelf_expired_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_clear_shelf_expired.label;</description>
+ <description id="checkin_auto_retarget_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_auto_retarget.label;</description>
+ <description id="checkin_auto_retarget_all_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_auto_retarget_all_ind.label;</description>
</vbox>
</vbox>
<spacer flex="1"/>
label="&staff.circ.checkin_overlay.checkin_auto_print_slips.label;" accesskey="&staff.circ.checkin_overlay.checkin_auto_print_slips.accesskey;"/>
<menuitem type="checkbox" id="checkin_clear_shelf_expired" oils_persist="checked" command="cmd_checkin_clear_shelf_expired"
label="&staff.circ.checkin_overlay.checkin_clear_shelf_expired.label;" accesskey="&staff.circ.checkin_overlay.checkin_clear_shelf_expired.accesskey;"/>
+ <menuitem type="checkbox" id="checkin_auto_retarget" oils_persist="checked" command="cmd_checkin_auto_retarget"
+ label="&staff.circ.checkin_overlay.checkin_auto_retarget.label;" accesskey="&staff.circ.checkin_overlay.checkin_auto_retarget.accesskey;"/>
+ <menuitem type="checkbox" id="checkin_auto_retarget_all" oils_persist="checked" command="cmd_checkin_auto_retarget"
+ label="&staff.circ.checkin_overlay.checkin_auto_retarget_all.label;" accesskey="&staff.circ.checkin_overlay.checkin_auto_retarget_all.accesskey;"/>
</menupopup>
</button>
</hbox>
.checkin_screen_amnesty_mode { }
.checkin_screen_checkin_auto_print_slips { }
.checkin_screen_checkin_clear_shelf_expired { }
+.checkin_screen_checkin_auto_retarget { }
+.checkin_screen_checkin_auto_retarget_all { }
#background-text { font-size: x-large; font-weight: bold; }
#do_not_alert_on_precat_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; }
#amnesty_mode_indicator { border: thick solid white; background-color: red; color: white; font-size: large; font-weight: bold; padding: 10px; padding-bottom: 25px; margin: 10px; }
#checkin_auto_print_slips_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; }
#checkin_clear_shelf_expired_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; }
+#checkin_auto_retarget_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; }
+#checkin_auto_retarget_all_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; }
.big_emphasis1 { font-weight: bold; font-size: x-large; }
.big_emphasis2 { font-weight: bold; font-size: large; }