TPac: holds placement on monographic parts
authorBill Erickson <berick@esilibrary.com>
Thu, 6 Oct 2011 18:34:03 +0000 (14:34 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Wed, 21 Dec 2011 16:18:06 +0000 (11:18 -0500)
Ability to place holds on monographic parts.  In the holds placement
form, if a record has parts, a parts selector will display in the form
allowing the user to optionally specificy a monographic part for the
hold.

If a record has no non-part copies, the user is required to select a
part, because, in such cases, the hold cannot be fulfilled without
selecting a part.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Jason Stephenson <jstephenson@mvlc.org>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/templates/opac/parts/place_hold.tt2
Open-ILS/src/templates/opac/parts/place_hold_result.tt2

index 48a07d8..30d2cba 100644 (file)
@@ -527,12 +527,14 @@ sub load_place_hold {
 
     $self->ctx->{page} = 'place_hold';
     my @targets = $cgi->param('hold_target');
+    my @parts = $cgi->param('part');
+
     $ctx->{hold_type} = $cgi->param('hold_type');
     $ctx->{default_pickup_lib} = $e->requestor->home_ou; # unless changed below
 
     return $self->generic_redirect unless @targets;
 
-    $logger->info("Looking at hold targets: @targets");
+    $logger->info("Looking at hold_type: " . $ctx->{hold_type} . " and targets: @targets");
 
     # if the staff client provides a patron barcode, fetch the patron
     if (my $bc = $self->cgi->cookie("patron_barcode")) {
@@ -551,9 +553,42 @@ sub load_place_hold {
     my $type_dispatch = {
         T => sub {
             my $recs = $e->batch_retrieve_biblio_record_entry(\@targets, {substream => 1});
+
             for my $id (@targets) { # force back into the correct order
                 my ($rec) = grep {$_->id eq $id} @$recs;
-                push(@hold_data, {target => $rec, record => $rec});
+
+                # NOTE: if tpac ever supports locked-down pickup libs,
+                # we'll need to pass a pickup_lib param along with the 
+                # record to filter the set of monographic parts.
+                my $parts = $U->simplereq(
+                    'open-ils.search',
+                    'open-ils.search.biblio.record_hold_parts', 
+                    {record => $rec->id}
+                );
+
+                # T holds on records that have parts are OK, but if the record has 
+                # no non-part copies, the hold will ultimately fail.  When that 
+                # happens, require the user to select a part.
+                my $part_required = 0;
+                if (@$parts) {
+                    my $np_copies = $e->json_query({
+                        select => { acp => [{column => 'id', transform => 'count', alias => 'count'}]}, 
+                        from => {acp => {acn => {}, acpm => {type => 'left'}}}, 
+                        where => {
+                            '+acp' => {deleted => 'f'},
+                            '+acn' => {deleted => 'f', record => $rec->id}, 
+                            '+acpm' => {id => undef}
+                        }
+                    });
+                    $part_required = 1 if $np_copies->[0]->{count} == 0;
+                }
+
+                push(@hold_data, {
+                    target => $rec, 
+                    record => $rec,
+                    parts => $parts,
+                    part_required => $part_required
+                });
             }
         },
         V => sub {
@@ -621,6 +656,7 @@ sub load_place_hold {
     $ctx->{orig_params} = $cgi->Vars;
     delete $ctx->{orig_params}{submit};
     delete $ctx->{orig_params}{hold_target};
+    delete $ctx->{orig_params}{part};
 
     my $usr = $e->requestor->id;
 
@@ -638,11 +674,63 @@ sub load_place_hold {
         }
     }
 
+    # target_id is the true target_id for holds placement.  
+    # needed for attempt_hold_placement()
+    # With the exception of P-type holds, target_id == target->id.
+    $_->{target_id} = $_->{target}->id for @hold_data;
+
+    if ($ctx->{hold_type} eq 'T') {
+
+        # Much like quantum wave-particles, P-type holds pop into 
+        # and out of existence at the user's whim.  For our purposes,
+        # we treat such holds as T(itle) holds with a selected_part 
+        # designation.  When the time comes to pass the hold information 
+        # off for holds possibility testing and placement, make it look 
+        # like a real P-type hold.
+        my (@p_holds, @t_holds);
+        
+        for my $idx (0..$#parts) {
+            my $hdata = $hold_data[$idx];
+            if (my $part = $parts[$idx]) {
+                $hdata->{target_id} = $part;
+                $hdata->{selected_part} = $part;
+                push(@p_holds, $hdata);
+            } else {
+                push(@t_holds, $hdata);
+            }
+        }
+
+        $self->attempt_hold_placement($usr, $pickup_lib, 'P', @p_holds) if @p_holds;
+        $self->attempt_hold_placement($usr, $pickup_lib, 'T', @t_holds) if @t_holds;
+
+    } else {
+        $self->attempt_hold_placement($usr, $pickup_lib, $ctx->{hold_type}, @hold_data);
+    }
+
+    # NOTE: we are leaving the staff-placed patron barcode cookie 
+    # in place.  Otherwise, it's not possible to place more than 
+    # one hold for the patron within a staff/patron session.  This 
+    # does leave the barcode to linger longer than is ideal, but 
+    # normal staff work flow will cause the cookie to be replaced 
+    # with each new patron anyway.
+    # TODO: See about getting the staff client to clear the cookie
+
+    # return to the place_hold page so the results of the hold
+    # placement attempt can be reported to the user
+    return Apache2::Const::OK;
+}
+
+sub attempt_hold_placement {
+    my ($self, $usr, $pickup_lib, $hold_type, @hold_data) = @_;
+    my $cgi = $self->cgi;
+    my $ctx = $self->ctx;
+    my $e = $self->editor;
+
     # First see if we should warn/block for any holds that 
     # might have locally available items.
     for my $hdata (@hold_data) {
         my ($local_block, $local_alert) = $self->local_avail_concern(
-            $hdata->{target}->id, $ctx->{hold_type}, $pickup_lib);
+            $hdata->{target_id}, $hold_type, $pickup_lib);
     
         if ($local_block) {
             $hdata->{hold_failed} = 1;
@@ -653,11 +741,10 @@ sub load_place_hold {
         }
     }
 
-
     my $method = 'open-ils.circ.holds.test_and_create.batch';
     $method .= '.override' if $cgi->param('override');
 
-    my @create_targets = map {$_->{target}->id} (grep { !$_->{hold_failed} } @hold_data);
+    my @create_targets = map {$_->{target_id}} (grep { !$_->{hold_failed} } @hold_data);
 
     if(@create_targets) {
 
@@ -667,7 +754,7 @@ sub load_place_hold {
             $e->authtoken, 
             {   patronid => $usr, 
                 pickup_lib => $pickup_lib, 
-                hold_type => $ctx->{hold_type}
+                hold_type => $hold_type
             }, 
             \@create_targets
         );
@@ -682,7 +769,7 @@ sub load_place_hold {
                 last;
             }
 
-            my ($hdata) = grep {$_->{target}->id eq $resp->{target}} @hold_data;
+            my ($hdata) = grep {$_->{target_id} eq $resp->{target}} @hold_data;
             my $result = $resp->{result};
 
             if ($U->event_code($result)) {
@@ -714,18 +801,6 @@ sub load_place_hold {
 
         $bses->kill_me;
     }
-
-    # NOTE: we are leaving the staff-placed patron barcode cookie 
-    # in place.  Otherwise, it's not possible to place more than 
-    # one hold for the patron within a staff/patron session.  This 
-    # does leave the barcode to linger longer than is ideal, but 
-    # normal staff work flow will cause the cookie to be replaced 
-    # with each new patron anyway.
-    # TODO: See about getting the staff client to clear the cookie
-
-    # return to the place_hold page so the results of the hold
-    # placement attempt can be reported to the user
-    return Apache2::Const::OK;
 }
 
 sub fetch_user_circs {
index 4580b9c..adb1483 100644 (file)
                 <td>
                     <input type="hidden" name="hold_target" value="[% hdata.target.id | html %]" />
                     <div class='hold-items-list-title'>[% attrs.title_extended | html %]</div>
+                    [% IF hdata.parts %]
+                        [% IF hdata.parts.size > 0 %]
+                        <div style='padding-left: 10px'>
+                            <span>[% hdata.part_required ? l('Select a Part:') : l('Select a Part (optional):') %]</span>
+                            <select name='part'>
+                                [% IF !hdata.part_required %]
+                                <option selected='selected' value=''>[% l('- All Parts -') %]</option>
+                                [% END %]
+                                [% FOR part IN hdata.parts %]
+                                <option value='[% part.id %]'>[% part.label | html %]</option>
+                                [% END %]
+                            </select>
+                        </div>
+                        [% ELSE %]
+                        <input type='hidden' name='part' value=''/>
+                        [% END %]
+                    [% END %]
                 </td>
             </tr>
         [% END %]
             [% PROCESS "opac/parts/org_selector.tt2";
                 PROCESS build_org_selector name='pickup_lib' value=ctx.default_pickup_lib id='pickup_lib' can_have_vols_only=1 %]
         </p>
-        <p>
-            [% |l %]If you use the Traveling Library Center (TLC) and ABC Express
-            services, please select "Outreach" to have the item delivered
-            during your scheduled visit.[% END %]
-        </p>
         <input type="submit" name="submit" value="[% l('Submit') %]" title="[% l('Submit') %]"
             alt="[% l('Submit') %]" class="opac-button" />
         &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
         <input type="reset" name="cancel" onclick="javascript:history.go(-1);"
             value="[% l('Cancel') %]" id="holds_cancel" class="opac-button" />
     </form>
-    <br /><br />
-    <p>
-        [% |l %]* If you need your item today, and it is checked in at your
-        library, please place your hold and then call your library to set it
-        aside. Placing a hold without calling the library will increase your
-        wait time.[% END %]
-        <br /><a href="#">[% l('Library phone numbers.') %]</a>
-    </p>
-    <p>
-        [% |l %]* For best possible service, we recommend keeping 
-        a printed copy of your most recent holds list.[% END %]
-    </p>
 </div>
 
index f92c38f..7b65859 100644 (file)
                 </td>
                 <td>
                     <div class='hold-items-list-title'>[% attrs.title_extended | html %]</div>
+                    [% IF hdata.parts %]
+                        [% IF hdata.parts.size > 0 %]
+                        <div style='padding-left: 10px'>
+                            <span>[% hdata.part_required ? l('Select a Part:') : l('Select a Part (optional):') %]</span>
+                            <select name='part'>
+                                [% IF !hdata.part_required %]
+                                <option [% UNLESS hdata.selected_part %]selected='selected'[% END %] value=''>[% l('- All Parts -') %]</option>
+                                [% END %]
+                                [% FOR part IN hdata.parts %]
+                                <option value='[% part.id %]' 
+                                    [% IF (hdata.selected_part || '') == part.id %]selected='selected'[% END %]>[% part.label | html %]</option>
+                                [% END %]
+                            </select>
+                        </div>
+                        [% ELSE %]
+                        <input type='hidden' name='part' value=''/>
+                        [% END %]
+                    [% END %]
                     <div>
                         [% IF hdata.hold_success %]