From b46a12a7bbeb690d7e168b4f8da7b6339d572821 Mon Sep 17 00:00:00 2001 From: Simon Hieu Mai Date: Mon, 22 Jul 2013 09:43:24 -0400 Subject: [PATCH] LP#1203759: Check-in modifier to retartget any branches within a system/library on new copy checkin When a new item (its status is "In progress") is checked in, any holds at any branches within a system/library will be searched and re-targeted. The search and retartget stops when a hold is found that the item can fill. Signed-off-by: Simon Hieu Mai --- .../lib/OpenILS/Application/Circ/Circulate.pm | 86 ++++++++++++++++++++++ Open-ILS/web/opac/locale/en-US/lang.dtd | 1 + Open-ILS/xul/staff_client/server/circ/checkin.js | 14 ++++ .../staff_client/server/circ/checkin_overlay.xul | 4 + Open-ILS/xul/staff_client/server/skin/circ.css | 2 + 5 files changed, 107 insertions(+) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm index ff50ed3797..6624723e34 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm @@ -2488,6 +2488,91 @@ sub checkin_retarget { } } +# Retarget system holds at checkin +sub checkin_retarget_system { + my $self = shift; + return unless $self->system_holds =~ 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 $U->is_true($self->copy->holdable); # Not holdable, shouldn't capture holds. + my $status = $U->copy_status($self->copy->status); + return unless $U->is_true($status->holdable); # Current status not holdable means no hold will ever target the item + # Specifically target items that are likely new (by status ID) + return unless $status->id == OILS_COPY_STATUS_IN_PROCESS; + my $location = $self->copy->location; + if(!ref($location)) { + $location = $self->editor->retrieve_asset_copy_location($self->copy->location); + $self->copy->location($location); + } + return unless $U->is_true($location->holdable); # Don't bother on non-holdable locations + + # 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 + }); + + # 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 + + # Check for parts on this copy + my $parts = $self->editor->search_asset_copy_part_map({ target_copy => $self->copy->id }); + my %parts_hash = (); + %parts_hash = map {$_->part, 1} @$parts if @$parts; + + # 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); + if(@$parts) { # We have parts? + # Skip title holds + next if ($_->{hold_type} eq 'T'); + # Skip part holds for parts not on this copy + next if ($_->{hold_type} eq 'P' and not $parts_hash{$_->{target}}); + } else { + # No parts, no part holds + next if ($_->{hold_type} eq 'P'); + } + # 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; $self->log_me("do_checkin()"); @@ -2498,6 +2583,7 @@ sub do_checkin { $self->check_transit_checkin_interval; $self->checkin_retarget; + $self->checkin_retarget_system; # the renew code and mk_env should have already found our circulation object unless( $self->circ ) { diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index e9f9f1ccb1..01ba3e2219 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -2486,6 +2486,7 @@ + diff --git a/Open-ILS/xul/staff_client/server/circ/checkin.js b/Open-ILS/xul/staff_client/server/circ/checkin.js index 3f1d976404..516b70ce2f 100644 --- a/Open-ILS/xul/staff_client/server/circ/checkin.js +++ b/Open-ILS/xul/staff_client/server/circ/checkin.js @@ -458,6 +458,17 @@ circ.checkin.prototype = { document.getElementById('checkin_barcode_entry_textbox').focus(); return true; } ], + 'cmd_checkin_system_holds' : [ ['command'], function(ev) { + dump('in cmd_checkin_system_holds\n'); + var bg = document.getElementById('background'); + var cb = document.getElementById('checkin_system_holds'); + var ind = document.getElementById('checkin_system_holds_indicator'); + var cn = 'checkin_screen_checkin_system_holds'; + if (cb.getAttribute('checked') == 'true') { addCSSClass(bg,cn); } else { removeCSSClass(bg,cn); } + ind.hidden = cb.getAttribute('checked') != 'true'; + document.getElementById('checkin_barcode_entry_textbox').focus(); + return true; + } ], 'cmd_checkin_local_hold_as_transit' : [ ['command'], function(ev) { dump('in cmd_checkin_local_hold_as_transit\n'); var bg = document.getElementById('background'); @@ -646,6 +657,9 @@ circ.checkin.prototype = { var hold_as_transit = document.getElementById('checkin_local_hold_as_transit'); if (hold_as_transit) hold_as_transit = hold_as_transit.getAttribute('checked') == 'true'; if (hold_as_transit) params.hold_as_transit = 1; + var system_holds = document.getElementById('checkin_system_holds'); + if (system_holds) system_holds = system_holds.getAttribute('checked') == 'true'; + if (system_holds) params.system_holds = 'retarget'; circ.util.checkin_via_barcode( ses(), params, diff --git a/Open-ILS/xul/staff_client/server/circ/checkin_overlay.xul b/Open-ILS/xul/staff_client/server/circ/checkin_overlay.xul index 9cc6b67f20..8ebc7162e9 100644 --- a/Open-ILS/xul/staff_client/server/circ/checkin_overlay.xul +++ b/Open-ILS/xul/staff_client/server/circ/checkin_overlay.xul @@ -39,6 +39,7 @@ + @@ -83,6 +84,7 @@ + @@ -191,6 +193,8 @@ label="&staff.circ.checkin_overlay.checkin_auto_retarget_all.label;" accesskey="&staff.circ.checkin_overlay.checkin_auto_retarget_all.accesskey;"/> + diff --git a/Open-ILS/xul/staff_client/server/skin/circ.css b/Open-ILS/xul/staff_client/server/skin/circ.css index 347251504d..202cf834a2 100644 --- a/Open-ILS/xul/staff_client/server/skin/circ.css +++ b/Open-ILS/xul/staff_client/server/skin/circ.css @@ -49,6 +49,7 @@ treechildren::-moz-tree-row(backdate_failed,selected) { .checkin_screen_checkin_auto_retarget { } .checkin_screen_checkin_auto_retarget_all { } .checkin_screen_checkin_local_hold_as_transit { } +.checkin_screen_checkin_system_holds { } #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; } @@ -59,6 +60,7 @@ treechildren::-moz-tree-row(backdate_failed,selected) { #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; } #checkin_local_hold_as_transit_indicator { background-color: -moz-dialog; color: -moz-dialog-text; font-size: large; font-weight: bold; } +#checkin_system_holds_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; } -- 2.11.0