Bug 23659: Add DefaultHoldPickupLocation system preference
[srvgit] / reserve / request.pl
index 9bad2e8..b6bdb0b 100755 (executable)
@@ -39,7 +39,6 @@ use C4::Koha qw( getitemtypeimagelocation );
 use C4::Serials qw( CountSubscriptionFromBiblionumber );
 use C4::Circulation qw( GetTransfers _GetCircControlBranch GetBranchItemRule );
 use Koha::DateUtils qw( dt_from_string output_pref );
-use C4::Utils::DataTables::Members;
 use C4::Search qw( enabled_staff_search_views );
 
 use Koha::Biblios;
@@ -51,6 +50,7 @@ use Koha::Items;
 use Koha::ItemTypes;
 use Koha::Libraries;
 use Koha::Patrons;
+use Koha::Patron::Attribute::Types;
 use Koha::Clubs;
 use Koha::BackgroundJob::BatchCancelHold;
 
@@ -68,7 +68,12 @@ my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user(
 my $showallitems = $input->param('showallitems');
 my $pickup = $input->param('pickup');
 
-my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
+my $itemtypes = {
+    map {
+        $_->itemtype =>
+          { %{ $_->unblessed }, image_location => $_->image_location, notforloan => $_->notforloan }
+    } Koha::ItemTypes->search_with_localization->as_list
+};
 
 # Select borrowers infos
 my $findborrower = $input->param('findborrower');
@@ -124,7 +129,7 @@ elsif ( $action eq 'toggleSuspend' ) {
 }
 elsif ( $action eq 'cancelBulk' ) {
     my $cancellation_reason = $input->param("cancellation-reason");
-    my @hold_ids            = split ',', $input->param("ids");
+    my @hold_ids            = split( ',', scalar $input->param("ids"));
     my $params              = {
         reason   => $cancellation_reason,
         hold_ids => \@hold_ids,
@@ -139,25 +144,7 @@ elsif ( $action eq 'cancelBulk' ) {
 
 if ($findborrower) {
     my $patron = Koha::Patrons->find( { cardnumber => $findborrower } );
-    if ( $patron ) {
-        $borrowernumber_hold = $patron->borrowernumber;
-    } else {
-        my $dt_params = { iDisplayLength => -1 };
-        my $results = C4::Utils::DataTables::Members::search(
-            {
-                searchmember => $findborrower,
-                dt_params => $dt_params,
-            }
-        );
-        my $borrowers = $results->{patrons};
-        if ( scalar @$borrowers == 1 ) {
-            $borrowernumber_hold = $borrowers->[0]->{borrowernumber};
-        } elsif ( @$borrowers ) {
-            $template->param( borrowers => $borrowers );
-        } else {
-            $messageborrower = "'$findborrower'";
-        }
-    }
+    $borrowernumber_hold = $patron->borrowernumber if $patron;
 }
 
 if($findclub) {
@@ -165,13 +152,10 @@ if($findclub) {
     if( $club ) {
         $club_hold = $club->id;
     } else {
-        my @clubs = Koha::Clubs->search(
-            [
-                { name        => { like => '%' . $findclub . '%' } },
-                { description => { like => '%' . $findclub . '%' } }
-            ]
-        )->filter_out_empty->as_list;
-
+        my @clubs = Koha::Clubs->search( [
+            { name => { like => '%'.$findclub.'%' } },
+            { description => { like => '%'.$findclub.'%' } }
+        ] )->as_list;
         if( scalar @clubs == 1 ) {
             $club_hold = $clubs[0]->id;
         } elsif ( @clubs ) {
@@ -182,23 +166,13 @@ if($findclub) {
     }
 }
 
-my @biblionumbers = ();
-my $biblionumber = $input->param('biblionumber');
-my $biblionumbers = $input->param('biblionumbers');
-if ( $biblionumbers ) {
-    @biblionumbers = split '/', $biblionumbers;
-} else {
-    push @biblionumbers, $input->multi_param('biblionumber');
-}
+my @biblionumbers = $input->multi_param('biblionumber');
 
 my $multi_hold = @biblionumbers > 1;
 $template->param(
     multi_hold => $multi_hold,
 );
 
-# If we are coming from the search result and only 1 is selected
-$biblionumber ||= $biblionumbers[0] unless $multi_hold;
-
 # If we have the borrowernumber because we've performed an action, then we
 # don't want to try to place another reserve.
 if ($borrowernumber_hold && !$action) {
@@ -320,6 +294,7 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
     my $itemdata_ccode = 0;
     my @biblioloop = ();
     my $no_reserves_allowed = 0;
+    my $num_bibs_available = 0;
     foreach my $biblionumber (@biblionumbers) {
         next unless $biblionumber =~ m|^\d+$|;
 
@@ -332,7 +307,6 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
             last;
         }
 
-        my $force_hold_level;
         if ( $patron ) {
             { # CanBookBeReserved
                 my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber );
@@ -359,6 +333,10 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
                     $template->param( $canReserve->{status} => 1);
                     $biblioloopiter{ $canReserve->{status} } = 1;
                 }
+                elsif ( $canReserve->{status} eq 'recall' ) {
+                    $template->param( $canReserve->{status} => 1 );
+                    $biblioloopiter{ $canReserve->{status} } = 1;
+                }
                 else {
                     $biblioloopiter{ $canReserve->{status} } = 1;
                 }
@@ -376,9 +354,7 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
                     found          => undef,
                 }
             );
-            $force_hold_level = $holds->forced_hold_level();
-            $biblioloopiter{force_hold_level} = $force_hold_level;
-            $template->param( force_hold_level => $force_hold_level );
+            $template->param( force_hold_level => $holds->forced_hold_level() );
 
             # For a librarian to be able to place multiple record holds for a patron for a record,
             # we must find out what the maximum number of holds they can place for the patron is
@@ -389,223 +365,185 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
             $template->param( remaining_holds_for_record => $remaining_holds_for_record );
         }
 
-
-        my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
-        my $totalcount = $count;
-
         # adding a fixed value for priority options
-        my $fixedRank = $count+1;
+        my $fixedRank = $biblio->holds->count + 1;
 
-        my %itemnumbers_of_biblioitem;
+        my @items = $biblio->items->as_list;
 
-        my @hostitems = get_hostitemnumbers_of($biblionumber);
-        my @itemnumbers;
-        if (@hostitems){
-            $template->param('hostitemsflag' => 1);
-            push(@itemnumbers, @hostitems);
+        my @host_items = $biblio->host_items->as_list;
+        if (@host_items) {
+            push @items, @host_items;
         }
 
-        my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
-
-        unless ( $items->count ) {
+        unless ( @items ) {
             # FIXME Then why do we continue?
             $template->param('noitems' => 1) unless ( $multi_hold );
             $biblioloopiter{noitems} = 1;
         }
 
-        ## Here we go backwards again to create hash of biblioitemnumber to itemnumbers
-        ## this is important when we have analytic items which may be on another record
-        my ( $iteminfos_of );
-        while ( my $item = $items->next ) {
-            $item = $item->unblessed;
-            my $biblioitemnumber = $item->{biblioitemnumber};
-            my $itemnumber = $item->{itemnumber};
-            push( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} }, $itemnumber );
-            $iteminfos_of->{$itemnumber} = $item;
-        }
-
-        my @biblioitemnumbers = keys %itemnumbers_of_biblioitem;
-
-        my $biblioiteminfos_of = {
-            map {
-                my $biblioitem = $_;
-                ( $biblioitem->{biblioitemnumber} => $biblioitem )
-              } @{ Koha::Biblioitems->search(
-                    { biblioitemnumber => { -in => \@biblioitemnumbers } },
-                    { select => ['biblionumber', 'biblioitemnumber', 'publicationyear', 'itemtype']}
-                )->unblessed
-              }
-        };
-
         if ( $club_hold or $borrowernumber_hold ) {
-            my @bibitemloop;
-
             my @available_itemtypes;
-            foreach my $biblioitemnumber (@biblioitemnumbers) {
-                my $biblioitem = $biblioiteminfos_of->{$biblioitemnumber};
-                my $num_available = 0;
-                my $num_override  = 0;
-                my $hiddencount   = 0;
-                my $num_alreadyheld = 0;
-
-                $biblioitem->{force_hold_level} = $force_hold_level;
-
-                if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
-                    $biblioitem->{hostitemsflag} = 1;
+            my $num_items_available = 0;
+            my $num_override  = 0;
+            my $hiddencount   = 0;
+            my $num_alreadyheld = 0;
+
+            # iterating through all items first to check if any of them available
+            # to pass this value further inside down to IsAvailableForItemLevelRequest to
+            # it's complicated logic to analyse.
+            # (before this loop was inside that sub loop so it was O(n^2) )
+            my $items_any_available;
+            $items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblio->biblionumber, patron => $patron })
+                if $patron;
+
+            for my $item_object ( @items ) {
+                my $do_check;
+                my $item = $item_object->unblessed;
+                if ( $patron ) {
+                    $do_check = $patron->do_check_for_previous_checkout($item) if $wants_check;
+                    if ( $do_check && $wants_check ) {
+                        $item->{checked_previously} = $do_check;
+                        if ( $multi_hold ) {
+                            $biblioloopiter{checked_previously} = $do_check;
+                        } else {
+                            $template->param( checked_previously => $do_check );
+                        }
+                    }
                 }
 
-                $biblioloopiter{description} = $biblioitem->{description};
-                $biblioloopiter{itypename}   = $biblioitem->{description};
-                if ( $biblioitem->{itemtype} ) {
+                $item->{itemtype} = $itemtypes->{ $item_object->effective_itemtype };
 
-                    $biblioitem->{description} =
-                      $itemtypes->{ $biblioitem->{itemtype} }{description};
-
-                    $biblioloopiter{imageurl} =
-                      getitemtypeimagelocation( 'intranet',
-                        $itemtypes->{ $biblioitem->{itemtype} }{imageurl} );
+                if($item->{biblionumber} ne $biblio->biblionumber){
+                    $item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
                 }
 
-                # iterating through all items first to check if any of them available
-                # to pass this value further inside down to IsAvailableForItemLevelRequest to
-                # it's complicated logic to analyse.
-                # (before this loop was inside that sub loop so it was O(n^2) )
-                my $items_any_available;
-                $items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblioitem->{biblionumber}, patron => $patron })
-                    if $patron;
-
-                foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
-                    my $item = $iteminfos_of->{$itemnumber};
-                    my $do_check;
-                    if ( $patron ) {
-                        $do_check = $patron->do_check_for_previous_checkout($item) if $wants_check;
-                        if ( $do_check && $wants_check ) {
-                            $item->{checked_previously} = $do_check;
-                            if ( $multi_hold ) {
-                                $biblioloopiter{checked_previously} = $do_check;
-                            } else {
-                                $template->param( checked_previously => $do_check );
-                            }
-                        }
-                    }
-                    $item->{force_hold_level} = $force_hold_level;
-
-                    unless (C4::Context->preference('item-level_itypes')) {
-                        $item->{itype} = $biblioitem->{itemtype};
-                    }
+                # if the item is currently on loan, we display its return date and
+                # change the background color
+                my $issue = $item_object->checkout;
+                if ( $issue ) { # FIXME must be moved to the template
+                    $item->{date_due} = $issue->date_due;
+                    $item->{backgroundcolor} = 'onloan';
+                }
 
-                    $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
-                    $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
-                    $item->{homebranch} = $item->{homebranch};
+                # checking reserve
+                my $holds = $item_object->current_holds;
+                if ( my $first_hold = $holds->next ) {
+                    my $p = Koha::Patrons->find( $first_hold->borrowernumber );
 
-                    # if the holdingbranch is different than the homebranch, we show the
-                    # holdingbranch of the document too
-                    if ( $item->{homebranch} ne $item->{holdingbranch} ) {
-                        $item->{holdingbranch} = $item->{holdingbranch};
-                    }
+                    $item->{backgroundcolor} = 'reserved';
+                    $item->{reservedate}     = output_pref({ dt => dt_from_string( $first_hold->reservedate ), dateonly => 1 }); # FIXME Should be formatted in the template
+                    $item->{ReservedFor}     = $p;
+                    $item->{ExpectedAtLibrary}     = $first_hold->branchcode;
+                    $item->{waitingdate} = $first_hold->waitingdate;
+                }
 
-                    if($item->{biblionumber} ne $biblionumber){
-                        $item->{hostitemsflag} = 1;
-                        $item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
-                    }
+                # Management of the notforloan document
+                if ( $item->{notforloan} ) {
+                    $item->{backgroundcolor} = 'other';
+                }
 
-                    # if the item is currently on loan, we display its return date and
-                    # change the background color
-                    my $issue = Koha::Checkouts->find( { itemnumber => $itemnumber } );
-                    if ( $issue ) {
-                        $item->{date_due} = $issue->date_due;
-                        $item->{backgroundcolor} = 'onloan';
+                # Management of lost or long overdue items
+                if ( $item->{itemlost} ) {
+                    $item->{backgroundcolor} = 'other';
+                    if ($logged_in_patron->category->hidelostitems && !$showallitems) {
+                        $item->{hide} = 1;
+                        $hiddencount++;
                     }
+                }
 
-                    # checking reserve
-                    my $item_object = Koha::Items->find( $itemnumber );
-                    my $holds = $item_object->current_holds;
-                    if ( my $first_hold = $holds->next ) {
-                        my $p = Koha::Patrons->find( $first_hold->borrowernumber );
-
-                        $item->{backgroundcolor} = 'reserved';
-                        $item->{reservedate}     = output_pref({ dt => dt_from_string( $first_hold->reservedate ), dateonly => 1 }); # FIXME Should be formatted in the template
-                        $item->{ReservedFor}     = $p;
-                        $item->{ExpectedAtLibrary}     = $first_hold->branchcode;
-                        $item->{waitingdate} = $first_hold->waitingdate;
-                    }
+                # Check the transit status
+                my ( $transfertwhen, $transfertfrom, $transfertto ) =
+                  GetTransfers($item_object->itemnumber); # FIXME replace with get_transfer
 
-                    # Management of the notforloan document
-                    if ( $item->{notforloan} ) {
-                        $item->{backgroundcolor} = 'other';
-                    }
+                if ( defined $transfertwhen && $transfertwhen ne '' ) {
+                    $item->{transfertwhen} = output_pref({ dt => dt_from_string( $transfertwhen ), dateonly => 1 });
+                    $item->{transfertfrom} = $transfertfrom;
+                    $item->{transfertto} = $transfertto;
+                    $item->{nocancel} = 1;
+                }
 
-                    # Management of lost or long overdue items
-                    if ( $item->{itemlost} ) {
-                        $item->{backgroundcolor} = 'other';
-                        if ($logged_in_patron->category->hidelostitems && !$showallitems) {
-                            $item->{hide} = 1;
-                            $hiddencount++;
+                # If there is no loan, return and transfer, we show a checkbox.
+                $item->{notforloanitype} = $item->{itemtype}->{notforloan};
+                $item->{notforloan} ||= 0;
+
+                # if independent branches is on we need to check if the person can reserve
+                # for branches they arent logged in to
+                if ( C4::Context->preference("IndependentBranches") ) {
+                    if (! C4::Context->preference("canreservefromotherbranches")){
+                        # can't reserve items so need to check if item homebranch and userenv branch match if not we can't reserve
+                        my $userenv = C4::Context->userenv;
+                        unless ( C4::Context->IsSuperLibrarian ) {
+                            $item->{cantreserve} = 1 if ( $item->{homebranch} ne $userenv->{branch} );
                         }
                     }
+                }
 
-                    # Check the transit status
-                    my ( $transfertwhen, $transfertfrom, $transfertto ) =
-                      GetTransfers($itemnumber);
+                if ( $patron ) {
+                    my $patron_unblessed = $patron->unblessed;
+                    my $branch = C4::Circulation::_GetCircControlBranch($item, $patron_unblessed);
 
-                    if ( defined $transfertwhen && $transfertwhen ne '' ) {
-                        $item->{transfertwhen} = output_pref({ dt => dt_from_string( $transfertwhen ), dateonly => 1 });
-                        $item->{transfertfrom} = $transfertfrom;
-                        $item->{transfertto} = $transfertto;
-                        $item->{nocancel} = 1;
-                    }
+                    my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} );
 
-                    # If there is no loan, return and transfer, we show a checkbox.
-                    $item->{notforloan} ||= 0;
-
-                    # if independent branches is on we need to check if the person can reserve
-                    # for branches they arent logged in to
-                    if ( C4::Context->preference("IndependentBranches") ) {
-                        if (! C4::Context->preference("canreservefromotherbranches")){
-                            # can't reserve items so need to check if item homebranch and userenv branch match if not we can't reserve
-                            my $userenv = C4::Context->userenv;
-                            unless ( C4::Context->IsSuperLibrarian ) {
-                                $item->{cantreserve} = 1 if ( $item->{homebranch} ne $userenv->{branch} );
-                            }
-                        }
-                    }
+                    $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
 
-                    if ( $patron ) {
-                        my $patron_unblessed = $patron->unblessed;
-                        my $branch = C4::Circulation::_GetCircControlBranch($item, $patron_unblessed);
+                    my $can_item_be_reserved = CanItemBeReserved( $patron, $item_object )->{status};
+                    $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
+                    $item->{not_holdable} ||= 'notforloan' if ( $item->{notforloanitype} || $item->{notforloan} > 0 );
 
-                        my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} );
 
-                        $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
+                    $item->{item_level_holds} = Koha::CirculationRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
 
-                        my $can_item_be_reserved = CanItemBeReserved( $patron->borrowernumber, $itemnumber )->{status};
-                        $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
+                    my $default_hold_pickup_location_pref = C4::Context->preference('DefaultHoldPickupLocation');
+                    my $default_pickup_branch;
+                    if( $default_hold_pickup_location_pref eq 'homebranch' ){
+                        $default_pickup_branch = $item->{homebranch};
+                    } elsif ( $default_hold_pickup_location_pref eq 'holdingbranch' ){
+                        $default_pickup_branch = $item->{holdingbranch};
+                    } else {
+                        $default_pickup_branch = C4::Context->userenv->{branch};
+                    }
 
-                        $item->{item_level_holds} = Koha::CirculationRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
+                    if (
+                           !$item->{cantreserve}
+                        && !$exceeded_maxreserves
+                        && $can_item_be_reserved eq 'OK'
+                        # items_any_available defined outside of the current loop,
+                        # so we avoiding loop inside IsAvailableForItemLevelRequest:
+                        && IsAvailableForItemLevelRequest($item_object, $patron, undef, $items_any_available)
+                      )
+                    {
+                        # Send the pickup locations count to the UI, the pickup locations will be pulled using the API
+                        my $pickup_locations = $item_object->pickup_locations({ patron => $patron });
+                        $item->{pickup_locations_count} = $pickup_locations->count;
+                        if ( $item->{pickup_locations_count} > 0 ) {
+                            $num_items_available++;
+                            $item->{available} = 1;
+                            # pass the holding branch for use as default
+                            my $default_pickup_location = $pickup_locations->search({ branchcode => $default_pickup_branch })->next;
+                            $item->{default_pickup_location} = $default_pickup_location;
+                        }
+                        else {
+                            $item->{available} = 0;
+                            $item->{not_holdable} = "no_valid_pickup_location";
+                        }
 
-                        if (
-                               !$item->{cantreserve}
-                            && !$exceeded_maxreserves
-                            && $can_item_be_reserved eq 'OK'
-                            # items_any_available defined outside of the current loop,
-                            # so we avoiding loop inside IsAvailableForItemLevelRequest:
-                            && IsAvailableForItemLevelRequest($item_object, $patron, undef, $items_any_available)
-                          )
-                        {
+                        push( @available_itemtypes, $item->{itype} );
+                    }
+                    elsif ( C4::Context->preference('AllowHoldPolicyOverride') ) {
+                        # If AllowHoldPolicyOverride is set, it should override EVERY restriction, not just branch item rules
+                        # with the exception of itemAlreadyOnHold because, you know, the item is already on hold
+                        if ( $can_item_be_reserved ne 'itemAlreadyOnHold' ) {
                             # Send the pickup locations count to the UI, the pickup locations will be pulled using the API
                             my @pickup_locations = $item_object->pickup_locations({ patron => $patron })->as_list;
                             $item->{pickup_locations_count} = scalar @pickup_locations;
 
                             if ( @pickup_locations ) {
-                                $num_available++;
+                                $num_items_available++;
                                 $item->{available} = 1;
 
                                 my $default_pickup_location;
 
-                                # Default to logged-in, if valid
-                                if ( C4::Context->userenv->{branch} ) {
-                                    ($default_pickup_location) = grep { $_->branchcode eq C4::Context->userenv->{branch} } @pickup_locations;
-                                }
+                                ($default_pickup_location) = grep { $_->branchcode eq $default_pickup_branch } @pickup_locations;
 
                                 $item->{default_pickup_location} = $default_pickup_location;
                             }
@@ -613,67 +551,44 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
                                 $item->{available} = 0;
                                 $item->{not_holdable} = "no_valid_pickup_location";
                             }
+                        } else { $num_alreadyheld++ }
 
-                            push( @available_itemtypes, $item->{itype} );
-                        }
-                        elsif ( C4::Context->preference('AllowHoldPolicyOverride') ) {
-                            # If AllowHoldPolicyOverride is set, it should override EVERY restriction, not just branch item rules
-                            # with the exception of itemAlreadyOnHold because, you know, the item is already on hold
-                            if ( $can_item_be_reserved ne 'itemAlreadyOnHold' ) {
-                                # Send the pickup locations count to the UI, the pickup locations will be pulled using the API
-                                my $pickup_locations = $item_object->pickup_locations({ patron => $patron });
-                                $item->{pickup_locations_count} = $pickup_locations->count;
-                                if ( $item->{pickup_locations_count} > 0 ) {
-                                    $item->{override} = 1;
-                                    $num_override++;
-                                    # pass the holding branch for use as default
-                                    my $default_pickup_location = $pickup_locations->search({ branchcode => $item->{holdingbranch} })->next;
-                                    $item->{default_pickup_location} = $default_pickup_location;
-                                }
-                                else {
-                                    $item->{available} = 0;
-                                    $item->{not_holdable} = "no_valid_pickup_location";
-                                }
-                            } else { $num_alreadyheld++ }
-
-                            push( @available_itemtypes, $item->{itype} );
-                        }
-
+                        push( @available_itemtypes, $item->{itype} );
+                    } else {
                         # If none of the conditions hold true, then neither override nor available is set and the item cannot be checked
-
-                        # Show serial enumeration when needed
-                        if ($item->{enumchron}) {
-                            $itemdata_enumchron = 1;
-                        }
-                        # Show collection when needed
-                        if ($item->{ccode}) {
-                            $itemdata_ccode = 1;
-                        }
+                        $item->{available} = 0;
                     }
 
-                    push @{ $biblioitem->{itemloop} }, $item;
-                }
 
-                # While we can't override an alreay held item, we should be able to override the others
-                # Unless all items are already held
-                if ( $num_override > 0 && ($num_override + $num_alreadyheld) == scalar( @{ $biblioitem->{itemloop} } ) ) {
-                # That is, if all items require an override
-                    $template->param( override_required => 1 );
-                } elsif ( $num_available == 0 ) {
-                    $template->param( none_available => 1 );
-                    $biblioloopiter{warn} = 1;
-                    $biblioloopiter{none_avail} = 1;
+                    # Show serial enumeration when needed
+                    if ($item->{enumchron}) {
+                        $itemdata_enumchron = 1;
+                    }
+                    # Show collection when needed
+                    if ($item->{ccode}) {
+                        $itemdata_ccode = 1;
+                    }
                 }
-                $template->param( hiddencount => $hiddencount);
 
-                push @bibitemloop, $biblioitem;
+                push @{ $biblioloopiter{itemloop} }, $item;
             }
 
+            $biblioloopiter{biblioitem} = $biblio->biblioitem;
+
+            # While we can't override an alreay held item, we should be able to override the others
+            # Unless all items are already held
+            if ( $num_override > 0 && ($num_override + $num_alreadyheld) == scalar( @{ $biblioloopiter{itemloop} } ) ) {
+            # That is, if all items require an override
+                $template->param( override_required => 1 );
+            } elsif ( $num_items_available == 0 ) {
+                $template->param( none_available => 1 );
+                $biblioloopiter{warn} = 1;
+                $biblioloopiter{none_avail} = 1;
+            }
+            $template->param( hiddencount => $hiddencount);
+
             @available_itemtypes = uniq( @available_itemtypes );
-            $template->param(
-                bibitemloop         => \@bibitemloop,
-                available_itemtypes => \@available_itemtypes
-            );
+            $template->param( available_itemtypes => \@available_itemtypes );
         }
 
         # existingreserves building
@@ -682,7 +597,7 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
         $template->param( always_show_holds => $always_show_holds );
         my $show_holds_now = $input->param('show_holds_now');
         unless( (defined $always_show_holds && $always_show_holds eq 'DONT') && !$show_holds_now ){
-            my @reserves = Koha::Holds->search( { biblionumber => $biblionumber }, { order_by => 'priority' } );
+            my @reserves = Koha::Holds->search( { biblionumber => $biblionumber }, { order_by => 'priority' } )->as_list;
             foreach my $res (
                 sort {
                     my $a_found = $a->found() || '';
@@ -757,7 +672,8 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
                         );
 
         $biblioloopiter{biblionumber} = $biblionumber;
-        $biblioloopiter{title} = $biblio->title;
+        $biblioloopiter{title}  = $biblio->title;
+        $biblioloopiter{author} = $biblio->author;
         $biblioloopiter{rank} = $fixedRank;
         $biblioloopiter{reserveloop} = \@reserveloop;
 
@@ -772,24 +688,31 @@ if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
             $biblioloopiter{pickup_locations_codes} = [ map { $_->branchcode } @pickup_locations ];
         }
 
+        $num_bibs_available++ unless $biblioloopiter{none_avail};
         push @biblioloop, \%biblioloopiter;
     }
 
+    $template->param( no_bibs_available => 1 ) unless $num_bibs_available > 0;
+
     $template->param( biblioloop => \@biblioloop );
     $template->param( no_reserves_allowed => $no_reserves_allowed );
     $template->param( exceeded_maxreserves => $exceeded_maxreserves );
     $template->param( exceeded_holds_per_record => $exceeded_holds_per_record );
-    $template->param( subscriptionsnumber => CountSubscriptionFromBiblionumber($biblionumber));
+    # FIXME: getting just the first bib's result doesn't seem right
+    $template->param( subscriptionsnumber => CountSubscriptionFromBiblionumber($biblionumbers[0]));
 } elsif ( ! $multi_hold ) {
-    my $biblio = Koha::Biblios->find( $biblionumber );
+    my $biblio = Koha::Biblios->find( $biblionumbers[0] );
     $template->param( biblio => $biblio );
 }
+$template->param( biblionumbers => \@biblionumbers );
+
+$template->param(
+    attribute_type_codes => ( C4::Context->preference('ExtendedPatronAttributes')
+        ? [ Koha::Patron::Attribute::Types->search( { staff_searchable => 1 } )->get_column('code') ]
+        : []
+    ),
+);
 
-if ( $multi_hold ) {
-    $template->param( biblionumbers => join('/', @biblionumbers) );
-} else {
-    $template->param( biblionumber => $biblionumber || $biblionumbers[0] );
-}
 
 # pass the userenv branch if no pickup location selected
 $template->param( pickup => $pickup || C4::Context->userenv->{branch} );
@@ -801,6 +724,7 @@ if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
 $template->param(
     SuspendHoldsIntranet => C4::Context->preference('SuspendHoldsIntranet'),
     AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
+    borrowernumber => $borrowernumber_hold,
 );
 
 # printout the page