LP#1203759: Check-in modifier to retartget any branches within a system/library on... user/simonmai/checkin_retarget_system
authorSimon Hieu Mai <hieu.mai@mnsu.edu>
Mon, 22 Jul 2013 13:43:24 +0000 (09:43 -0400)
committerSimon Hieu Mai <hieu.mai@mnsu.edu>
Mon, 22 Jul 2013 13:43:24 +0000 (09:43 -0400)
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 <hieu.mai@mnsu.edu>
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Circulate.pm
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/server/circ/checkin.js
Open-ILS/xul/staff_client/server/circ/checkin_overlay.xul
Open-ILS/xul/staff_client/server/skin/circ.css

index ff50ed3..6624723 100644 (file)
@@ -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 ) {
index e9f9f1c..01ba3e2 100644 (file)
 <!ENTITY staff.circ.checkin_overlay.checkin_auto_retarget_all_ind.label "Always Retarget Local Holds">
 <!ENTITY staff.circ.checkin_overlay.checkin_local_hold_as_transit.label "Capture Local Holds as Transits">
 <!ENTITY staff.circ.checkin_overlay.checkin_local_hold_as_transit.accesskey "L">
+<!ENTITY staff.circ.checkin_overlay.checkin_system_holds.label "Retarget System (Library) 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">
index 3f1d976..516b70c 100644 (file)
@@ -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,
index 9cc6b67..8ebc716 100644 (file)
@@ -39,6 +39,7 @@
     <command id="cmd_checkin_clear_shelf_expired" />
     <command id="cmd_checkin_auto_retarget" />
     <command id="cmd_checkin_local_hold_as_transit" />
+    <command id="cmd_checkin_system_holds" />
 </commandset>
 
 
@@ -83,6 +84,7 @@
                 <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>
                 <description id="checkin_local_hold_as_transit_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_local_hold_as_transit.label;</description>
+                <description id="checkin_system_holds_indicator" hidden="true">&staff.circ.checkin_overlay.checkin_system_holds.label;</description>
             </vbox>
         </vbox>
         <spacer flex="1"/>
                 label="&staff.circ.checkin_overlay.checkin_auto_retarget_all.label;" accesskey="&staff.circ.checkin_overlay.checkin_auto_retarget_all.accesskey;"/>
             <menuitem type="checkbox" id="checkin_local_hold_as_transit" oils_persist="checked" command="cmd_checkin_local_hold_as_transit"
                 label="&staff.circ.checkin_overlay.checkin_local_hold_as_transit.label;" accesskey="&staff.circ.checkin_overlay.checkin_local_hold_as_transit;"/>
+            <menuitem type="checkbox" id="checkin_system_holds" oils_persist="checked" command="cmd_checkin_system_holds"
+                label="&staff.circ.checkin_overlay.checkin_system_holds.label;"/>
         </menupopup>
     </button>
 </hbox>
index 3472515..202cf83 100644 (file)
@@ -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; }