Bug 28405: Add author for multi holds view
[srvgit] / reserve / request.pl
index 8936808..b78faa9 100755 (executable)
@@ -29,45 +29,44 @@ script to place reserves/requests
 use Modern::Perl;
 
 use CGI qw ( -utf8 );
-use List::MoreUtils qw/uniq/;
-use Date::Calc qw/Date_to_Days/;
-use C4::Output;
-use C4::Auth;
-use C4::Reserves;
-use C4::Biblio;
-use C4::Items;
-use C4::Koha;
-use C4::Serials;
-use C4::Circulation;
-use Koha::DateUtils;
+use List::MoreUtils qw( uniq );
+use Date::Calc qw( Date_to_Days );
+use C4::Output qw( output_html_with_http_headers );
+use C4::Auth qw( get_template_and_user );
+use C4::Reserves qw( RevertWaitingStatus AlterPriority ToggleLowestPriority ToggleSuspend CanBookBeReserved GetMaxPatronHoldsForRecord ItemsAnyAvailableAndNotRestricted CanItemBeReserved IsAvailableForItemLevelRequest );
+use C4::Items qw( get_hostitemnumbers_of );
+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::Members;
-use C4::Search;                # enabled_staff_search_views
+use C4::Search qw( enabled_staff_search_views );
 
 use Koha::Biblios;
-use Koha::DateUtils;
+use Koha::DateUtils qw( dt_from_string output_pref );
 use Koha::Checkouts;
 use Koha::Holds;
-use Koha::IssuingRules;
+use Koha::CirculationRules;
 use Koha::Items;
 use Koha::ItemTypes;
 use Koha::Libraries;
 use Koha::Patrons;
+use Koha::Clubs;
+use Koha::BackgroundJob::BatchCancelHold;
 
 my $dbh = C4::Context->dbh;
-my $input = new CGI;
+my $input = CGI->new;
 my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user(
     {
         template_name   => "reserve/request.tt",
         query           => $input,
         type            => "intranet",
-        authnotrequired => 0,
         flagsrequired   => { reserveforothers => 'place_holds' },
     }
 );
 
 my $showallitems = $input->param('showallitems');
-my $pickup = $input->param('pickup') || C4::Context->userenv->{'branch'};
+my $pickup = $input->param('pickup');
 
 my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
 
@@ -75,8 +74,12 @@ my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_l
 my $findborrower = $input->param('findborrower');
 $findborrower = '' unless defined $findborrower;
 $findborrower =~ s|,| |g;
+my $findclub = $input->param('findclub');
+$findclub = '' unless defined $findclub && !$findborrower;
 my $borrowernumber_hold = $input->param('borrowernumber') || '';
+my $club_hold = $input->param('club')||'';
 my $messageborrower;
+my $messageclub;
 my $warnings;
 my $messages;
 my $exceeded_maxreserves;
@@ -87,29 +90,51 @@ my $action = $input->param('action');
 $action ||= q{};
 
 if ( $action eq 'move' ) {
-  my $where           = $input->param('where');
-  my $reserve_id      = $input->param('reserve_id');
-  my $prev_priority   = $input->param('prev_priority');
-  my $next_priority   = $input->param('next_priority');
-  my $first_priority  = $input->param('first_priority');
-  my $last_priority   = $input->param('last_priority');
-  my $hold_itemnumber = $input->param('itemnumber');
-  if ( $prev_priority == 0 && $next_priority == 1 ){
-      C4::Reserves::RevertWaitingStatus({ itemnumber => $hold_itemnumber });
-  } else {
-      AlterPriority( $where, $reserve_id, $prev_priority, $next_priority, $first_priority, $last_priority );
-  }
-} elsif ( $action eq 'cancel' ) {
-  my $reserve_id = $input->param('reserve_id');
-  my $hold = Koha::Holds->find( $reserve_id );
-  $hold->cancel if $hold;
-} elsif ( $action eq 'setLowestPriority' ) {
-  my $reserve_id = $input->param('reserve_id');
-  ToggleLowestPriority( $reserve_id );
-} elsif ( $action eq 'toggleSuspend' ) {
-  my $reserve_id = $input->param('reserve_id');
-  my $suspend_until  = $input->param('suspend_until');
-  ToggleSuspend( $reserve_id, $suspend_until );
+    my $where           = $input->param('where');
+    my $reserve_id      = $input->param('reserve_id');
+    my $prev_priority   = $input->param('prev_priority');
+    my $next_priority   = $input->param('next_priority');
+    my $first_priority  = $input->param('first_priority');
+    my $last_priority   = $input->param('last_priority');
+    my $hold_itemnumber = $input->param('itemnumber');
+    if ( $prev_priority == 0 && $next_priority == 1 ) {
+        C4::Reserves::RevertWaitingStatus( { itemnumber => $hold_itemnumber } );
+    }
+    else {
+        AlterPriority(
+            $where,         $reserve_id,     $prev_priority,
+            $next_priority, $first_priority, $last_priority
+        );
+    }
+}
+elsif ( $action eq 'cancel' ) {
+    my $reserve_id          = $input->param('reserve_id');
+    my $cancellation_reason = $input->param("cancellation-reason");
+    my $hold                = Koha::Holds->find($reserve_id);
+    $hold->cancel( { cancellation_reason => $cancellation_reason } ) if $hold;
+}
+elsif ( $action eq 'setLowestPriority' ) {
+    my $reserve_id = $input->param('reserve_id');
+    ToggleLowestPriority($reserve_id);
+}
+elsif ( $action eq 'toggleSuspend' ) {
+    my $reserve_id    = $input->param('reserve_id');
+    my $suspend_until = $input->param('suspend_until');
+    ToggleSuspend( $reserve_id, $suspend_until );
+}
+elsif ( $action eq 'cancelBulk' ) {
+    my $cancellation_reason = $input->param("cancellation-reason");
+    my @hold_ids            = split ',', $input->param("ids");
+    my $params              = {
+        reason   => $cancellation_reason,
+        hold_ids => \@hold_ids,
+    };
+    my $job_id = Koha::BackgroundJob::BatchCancelHold->new->enqueue($params);
+
+    $template->param(
+        enqueued => 1,
+        job_id   => $job_id
+    );
 }
 
 if ($findborrower) {
@@ -135,6 +160,28 @@ if ($findborrower) {
     }
 }
 
+if($findclub) {
+    my $club = Koha::Clubs->find( { name => $findclub } );
+    if( $club ) {
+        $club_hold = $club->id;
+    } else {
+        my @clubs = Koha::Clubs->search(
+            [
+                { name        => { like => '%' . $findclub . '%' } },
+                { description => { like => '%' . $findclub . '%' } }
+            ]
+        )->filter_out_empty->as_list;
+
+        if( scalar @clubs == 1 ) {
+            $club_hold = $clubs[0]->id;
+        } elsif ( @clubs ) {
+            $template->param( clubs => \@clubs );
+        } else {
+            $messageclub = "'$findclub'";
+        }
+    }
+}
+
 my @biblionumbers = ();
 my $biblionumber = $input->param('biblionumber');
 my $biblionumbers = $input->param('biblionumbers');
@@ -144,9 +191,13 @@ if ( $biblionumbers ) {
     push @biblionumbers, $input->multi_param('biblionumber');
 }
 
-my $multihold = scalar $input->param('multi_hold');
-# FIXME multi_hold should not be a variable but depends on the number of elements in @biblionumbers
-$template->param(multi_hold => scalar $input->param('multi_hold'));
+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.
@@ -162,6 +213,8 @@ if ($borrowernumber_hold && !$action) {
     my $new_reserves_count = scalar( @biblionumbers );
 
     my $maxreserves = C4::Context->preference('maxreserves');
+    $template->param( maxreserves => $maxreserves );
+
     if ( $maxreserves
         && ( $reserves_count + $new_reserves_count > $maxreserves ) )
     {
@@ -179,14 +232,6 @@ if ($borrowernumber_hold && !$action) {
         );
     }
 
-    # we check the date expiry of the borrower (only if there is an expiry date, otherwise, set to 1 (warn)
-    my $expiry_date = $patron->dateexpiry;
-    my $expiry = 0; # flag set if patron account has expired
-    if ($expiry_date and $expiry_date ne '0000-00-00' and
-        Date_to_Days(split /-/,$date) > Date_to_Days(split /-/,$expiry_date)) {
-        $expiry = 1;
-    }
-
     # check if the borrower make the reserv in a different branch
     if ( $patron->branchcode ne C4::Context->userenv->{'branch'} ) {
         $diffbranch = 1;
@@ -195,7 +240,6 @@ if ($borrowernumber_hold && !$action) {
     my $amount_outstanding = $patron->account->balance;
     $template->param(
                 patron              => $patron,
-                expiry              => $expiry,
                 diffbranch          => $diffbranch,
                 messages            => $messages,
                 warnings            => $warnings,
@@ -203,457 +247,553 @@ if ($borrowernumber_hold && !$action) {
     );
 }
 
-$template->param( messageborrower => $messageborrower );
-
-# FIXME launch another time GetMember perhaps until (Joubu: Why?)
-my $patron = Koha::Patrons->find( $borrowernumber_hold );
-
-my $logged_in_patron = Koha::Patrons->find( $borrowernumber );
-
-my $wants_check;
-if ($patron) {
-    $wants_check = $patron->wants_check_for_previous_checkout;
-}
-my $itemdata_enumchron = 0;
-my $itemdata_ccode = 0;
-my @biblioloop = ();
-foreach my $biblionumber (@biblionumbers) {
-    next unless $biblionumber =~ m|^\d+$|;
-
-    my %biblioloopiter = ();
+if ($club_hold && !$borrowernumber_hold && !$action) {
+    my $club = Koha::Clubs->find($club_hold);
 
-    my $biblio = Koha::Biblios->find( $biblionumber );
+    my $enrollments = $club->club_enrollments;
 
-    my $force_hold_level;
-    if ( $patron ) {
-        { # CanBookBeReserved
-            my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber, $pickup );
-            if ( $canReserve->{status} eq 'OK' ) {
+    my $maxreserves = C4::Context->preference('maxreserves');
+    my $new_reserves_count = scalar( @biblionumbers );
 
-                #All is OK and we can continue
-            }
-            elsif ( $canReserve->{status} eq 'tooManyReserves' ) {
-                $exceeded_maxreserves = 1;
-                $template->param( maxreserves => $canReserve->{limit} );
-            }
-            elsif ( $canReserve->{status} eq 'tooManyHoldsForThisRecord' ) {
-                $exceeded_holds_per_record = 1;
-                $biblioloopiter{ $canReserve->{status} } = 1;
-            }
-            elsif ( $canReserve->{status} eq 'ageRestricted' ) {
-                $template->param( $canReserve->{status} => 1 );
-                $biblioloopiter{ $canReserve->{status} } = 1;
-            }
-            else {
-                $biblioloopiter{ $canReserve->{status} } = 1;
-            }
+    my @members;
+
+    while(my $enrollment = $enrollments->next) {
+        next if $enrollment->is_canceled;
+        my $member = { patron => $enrollment->patron };
+        my $reserves_count = $enrollment->patron->holds->count;
+        if ( $maxreserves
+            && ( $reserves_count + $new_reserves_count > $maxreserves ) )
+        {
+            $member->{new_reserves_allowed} = $maxreserves - $reserves_count > 0
+                ? $maxreserves - $reserves_count
+                : 0;
+            $member->{exceeded_maxreserves} = 1;
         }
-
-        # For multiple holds per record, if a patron has previously placed a hold,
-        # the patron can only place more holds of the same type. That is, if the
-        # patron placed a record level hold, all the holds the patron places must
-        # be record level. If the patron placed an item level hold, all holds
-        # the patron places must be item level
-        my $holds = Koha::Holds->search(
-            {
-                borrowernumber => $patron->borrowernumber,
-                biblionumber   => $biblionumber,
-                found          => undef,
-            }
-        );
-        $force_hold_level = $holds->forced_hold_level();
-        $biblioloopiter{force_hold_level} = $force_hold_level;
-        $template->param( force_hold_level => $force_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
-        my $max_holds_for_record = GetMaxPatronHoldsForRecord( $patron->borrowernumber, $biblionumber );
-        my $remaining_holds_for_record = $max_holds_for_record - $holds->count();
-        $biblioloopiter{remaining_holds_for_record} = $max_holds_for_record;
-        $template->param( max_holds_for_record => $max_holds_for_record );
-        $template->param( remaining_holds_for_record => $remaining_holds_for_record );
-
-        { # alreadypossession
-            # Check to see if patron is allowed to place holds on records where the
-            # patron already has an item from that record checked out
-            if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
-                && CheckIfIssuedToPatron( $patron->borrowernumber, $biblionumber ) )
-            {
-                $template->param( alreadypossession => 1, );
-            }
+        $member->{amount_outstanding} = $enrollment->patron->account->balance;
+        if ( $enrollment->patron->branchcode ne C4::Context->userenv->{'branch'} ) {
+            $member->{diffbranch} = 1;
         }
+
+        push @members, $member;
     }
 
+    $template->param(
+        club                => $club,
+        members             => \@members,
+        maxreserves         => $maxreserves,
+        new_reserves_count  => $new_reserves_count
+    );
+}
 
-    my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
-    my $totalcount = $count;
+unless ( $club_hold or $borrowernumber_hold ) {
+    $template->param( clubcount => Koha::Clubs->search->count );
+}
 
-    # FIXME think @optionloop, is maybe obsolete, or  must be switchable by a systeme preference fixed rank or not
-    # make priorities options
+$template->param(
+    messageborrower => $messageborrower,
+    messageclub     => $messageclub
+);
 
-    my @optionloop;
-    for ( 1 .. $count + 1 ) {
-        push(
-             @optionloop,
-             {
-              num      => $_,
-              selected => ( $_ == $count + 1 ),
-             }
-            );
+# Load the hold list if
+#  - we are searching for a patron or club and found one
+#  - we are not searching for anything
+if (   ( $findborrower && $borrowernumber_hold || $findclub && $club_hold )
+    || ( !$findborrower && !$findclub ) )
+{
+    # FIXME launch another time GetMember perhaps until (Joubu: Why?)
+    my $patron = Koha::Patrons->find( $borrowernumber_hold );
+
+    if ( $patron && $multi_hold ) {
+        my @multi_pickup_locations =
+          Koha::Biblios->search( { biblionumber => \@biblionumbers } )
+          ->pickup_locations( { patron => $patron } )->as_list;
+        $template->param( multi_pickup_locations => \@multi_pickup_locations );
     }
-    # adding a fixed value for priority options
-    my $fixedRank = $count+1;
 
-    my %itemnumbers_of_biblioitem;
+    my $logged_in_patron = Koha::Patrons->find( $borrowernumber );
 
-    my @hostitems = get_hostitemnumbers_of($biblionumber);
-    my @itemnumbers;
-    if (@hostitems){
-        $template->param('hostitemsflag' => 1);
-        push(@itemnumbers, @hostitems);
+    my $wants_check;
+    if ($patron) {
+        $wants_check = $patron->wants_check_for_previous_checkout;
     }
+    my $itemdata_enumchron = 0;
+    my $itemdata_ccode = 0;
+    my @biblioloop = ();
+    my $no_reserves_allowed = 0;
+    foreach my $biblionumber (@biblionumbers) {
+        next unless $biblionumber =~ m|^\d+$|;
+
+        my %biblioloopiter = ();
+
+        my $biblio = Koha::Biblios->find( $biblionumber );
+        unless ($biblio) {
+            $biblioloopiter{noitems} = 1;
+            $template->param('nobiblio' => 1);
+            last;
+        }
 
-    my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
+        my $force_hold_level;
+        if ( $patron ) {
+            { # CanBookBeReserved
+                my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber );
+                if ( $canReserve->{status} eq 'OK' ) {
 
-    unless ( $items->count ) {
-        # FIXME Then why do we continue?
-        $template->param('noitems' => 1);
-        $biblioloopiter{noitems} = 1;
-    }
+                    #All is OK and we can continue
+                }
+                elsif ( $canReserve->{status} eq 'noReservesAllowed' || $canReserve->{status} eq 'notReservable' ) {
+                    $no_reserves_allowed = 1;
+                }
+                elsif ( $canReserve->{status} eq 'tooManyReserves' ) {
+                    $exceeded_maxreserves = 1;
+                    $template->param( maxreserves => $canReserve->{limit} );
+                }
+                elsif ( $canReserve->{status} eq 'tooManyHoldsForThisRecord' ) {
+                    $exceeded_holds_per_record = 1;
+                    $biblioloopiter{ $canReserve->{status} } = 1;
+                }
+                elsif ( $canReserve->{status} eq 'ageRestricted' ) {
+                    $template->param( $canReserve->{status} => 1 );
+                    $biblioloopiter{ $canReserve->{status} } = 1;
+                }
+                elsif ( $canReserve->{status} eq 'alreadypossession' ) {
+                    $template->param( $canReserve->{status} => 1);
+                    $biblioloopiter{ $canReserve->{status} } = 1;
+                }
+                else {
+                    $biblioloopiter{ $canReserve->{status} } = 1;
+                }
+            }
 
-    ## Here we go backwards again to create hash of biblioitemnumber to itemnumbers,
-    ## when by definition all of the itemnumber have the same biblioitemnumber
-    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;
-    }
+            # For multiple holds per record, if a patron has previously placed a hold,
+            # the patron can only place more holds of the same type. That is, if the
+            # patron placed a record level hold, all the holds the patron places must
+            # be record level. If the patron placed an item level hold, all holds
+            # the patron places must be item level
+            my $holds = Koha::Holds->search(
+                {
+                    borrowernumber => $patron->borrowernumber,
+                    biblionumber   => $biblionumber,
+                    found          => undef,
+                }
+            );
+            $force_hold_level = $holds->forced_hold_level();
+            $biblioloopiter{force_hold_level} = $force_hold_level;
+            $template->param( force_hold_level => $force_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
+            my $max_holds_for_record = GetMaxPatronHoldsForRecord( $patron->borrowernumber, $biblionumber );
+            my $remaining_holds_for_record = $max_holds_for_record - $holds->count();
+            $biblioloopiter{remaining_holds_for_record} = $max_holds_for_record;
+            $template->param( max_holds_for_record => $max_holds_for_record );
+            $template->param( remaining_holds_for_record => $remaining_holds_for_record );
+        }
 
-    ## Should be same as biblionumber
-    my @biblioitemnumbers = keys %itemnumbers_of_biblioitem;
-
-    my $biblioiteminfos_of = {
-        map {
-            my $biblioitem = $_;
-            ( $biblioitem->{biblioitemnumber} => $biblioitem )
-          } @{ Koha::Biblioitems->search(
-                { biblioitemnumber => { -in => \@biblioitemnumbers } },
-                { select => ['biblioitemnumber', 'publicationyear', 'itemtype']}
-            )->unblessed
-          }
-    };
 
-    my @bibitemloop;
+        my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
+        my $totalcount = $count;
 
-    my @available_itemtypes;
-    foreach my $biblioitemnumber (@biblioitemnumbers) {
-        my $biblioitem = $biblioiteminfos_of->{$biblioitemnumber};
-        my $num_available = 0;
-        my $num_override  = 0;
-        my $hiddencount   = 0;
+        # adding a fixed value for priority options
+        my $fixedRank = $count+1;
 
-        $biblioitem->{force_hold_level} = $force_hold_level;
+        my %itemnumbers_of_biblioitem;
 
-        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
-            $biblioitem->{hostitemsflag} = 1;
+        my @hostitems = get_hostitemnumbers_of($biblionumber);
+        my @itemnumbers;
+        if (@hostitems){
+            $template->param('hostitemsflag' => 1);
+            push(@itemnumbers, @hostitems);
         }
 
-        $biblioloopiter{description} = $biblioitem->{description};
-        $biblioloopiter{itypename}   = $biblioitem->{description};
-        if ( $biblioitem->{itemtype} ) {
+        my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
 
-            $biblioitem->{description} =
-              $itemtypes->{ $biblioitem->{itemtype} }{description};
+        unless ( $items->count ) {
+            # FIXME Then why do we continue?
+            $template->param('noitems' => 1) unless ( $multi_hold );
+            $biblioloopiter{noitems} = 1;
+        }
 
-            $biblioloopiter{imageurl} =
-              getitemtypeimagelocation( 'intranet',
-                $itemtypes->{ $biblioitem->{itemtype} }{imageurl} );
+        ## 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;
         }
 
-        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 ( $multihold ) {
-                        $biblioloopiter{checked_previously} = $do_check;
-                    } else {
-                        $template->param( checked_previously => $do_check );
-                    }
+        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;
                 }
-            }
-            $item->{force_hold_level} = $force_hold_level;
-
-            unless (C4::Context->preference('item-level_itypes')) {
-                $item->{itype} = $biblioitem->{itemtype};
-            }
-
-            $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
-            $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
-            $item->{homebranch} = $item->{homebranch};
 
-            # 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};
-            }
+                $biblioloopiter{description} = $biblioitem->{description};
+                $biblioloopiter{itypename}   = $biblioitem->{description};
+                if ( $biblioitem->{itemtype} ) {
 
-            if($item->{biblionumber} ne $biblionumber){
-                $item->{hostitemsflag} = 1;
-                $item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
-            }
+                    $biblioitem->{description} =
+                      $itemtypes->{ $biblioitem->{itemtype} }{description};
 
-            # 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';
-            }
+                    $biblioloopiter{imageurl} =
+                      getitemtypeimagelocation( 'intranet',
+                        $itemtypes->{ $biblioitem->{itemtype} }{imageurl} );
+                }
 
-            # 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;
-            }
+                # 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;
 
-            # Management of the notforloan document
-            if ( $item->{notforloan} ) {
-                $item->{backgroundcolor} = 'other';
-            }
+                    unless (C4::Context->preference('item-level_itypes')) {
+                        $item->{itype} = $biblioitem->{itemtype};
+                    }
 
-            # Management of lost or long overdue items
-            if ( $item->{itemlost} ) {
-                $item->{backgroundcolor} = 'other';
-                if ($logged_in_patron->category->hidelostitems && !$showallitems) {
-                    $item->{hide} = 1;
-                    $hiddencount++;
-                }
-            }
+                    $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
+                    $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
+                    $item->{homebranch} = $item->{homebranch};
 
-            # Check the transit status
-            my ( $transfertwhen, $transfertfrom, $transfertto ) =
-              GetTransfers($itemnumber);
+                    # 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};
+                    }
 
-            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;
-            }
+                    if($item->{biblionumber} ne $biblionumber){
+                        $item->{hostitemsflag} = 1;
+                        $item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
+                    }
 
-            # 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} );
+                    # 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';
                     }
-                }
-            }
 
-            if ( $patron ) {
-                my $patron_unblessed = $patron->unblessed;
-                my $branch = C4::Circulation::_GetCircControlBranch($item, $patron_unblessed);
+                    # 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;
+                    }
 
-                my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} );
+                    # Management of the notforloan document
+                    if ( $item->{notforloan} ) {
+                        $item->{backgroundcolor} = 'other';
+                    }
 
-                $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
+                    # Management of lost or long overdue items
+                    if ( $item->{itemlost} ) {
+                        $item->{backgroundcolor} = 'other';
+                        if ($logged_in_patron->category->hidelostitems && !$showallitems) {
+                            $item->{hide} = 1;
+                            $hiddencount++;
+                        }
+                    }
 
-                my $can_item_be_reserved = CanItemBeReserved( $patron->borrowernumber, $itemnumber, $pickup );
-                $item->{not_holdable} = $can_item_be_reserved->{status} unless ( $can_item_be_reserved->{status} eq 'OK' );
+                    # Check the transit status
+                    my ( $transfertwhen, $transfertfrom, $transfertto ) =
+                      GetTransfers($itemnumber);
 
-                $item->{item_level_holds} = Koha::IssuingRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
+                    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;
+                    }
 
-                if (
-                       !$item->{cantreserve}
-                    && !$exceeded_maxreserves
-                    && IsAvailableForItemLevelRequest($item_object, $patron)
-                    && $can_item_be_reserved->{status} eq 'OK'
-                  )
-                {
-                    $item->{available} = 1;
-                    $num_available++;
+                    # 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} );
+                            }
+                        }
+                    }
 
-                    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' ) {
-                        $item->{override} = 1;
-                        $num_override++;
+                    if ( $patron ) {
+                        my $patron_unblessed = $patron->unblessed;
+                        my $branch = C4::Circulation::_GetCircControlBranch($item, $patron_unblessed);
+
+                        my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} );
+
+                        $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
+
+                        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->{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 })->as_list;
+                            $item->{pickup_locations_count} = scalar @pickup_locations;
+
+                            if ( @pickup_locations ) {
+                                $num_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;
+                                }
+
+                                $item->{default_pickup_location} = $default_pickup_location;
+                            }
+                            else {
+                                $item->{available} = 0;
+                                $item->{not_holdable} = "no_valid_pickup_location";
+                            }
+
+                            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} );
+                        }
+
+                        # 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;
+                        }
                     }
 
-                    push( @available_itemtypes, $item->{itype} );
+                    push @{ $biblioitem->{itemloop} }, $item;
                 }
 
-                # 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;
+                # 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;
                 }
-            }
-
-            push @{ $biblioitem->{itemloop} }, $item;
-        }
-
-        if ( $num_override == 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;
-        }
-        $template->param( hiddencount => $hiddencount);
+                $template->param( hiddencount => $hiddencount);
 
-        push @bibitemloop, $biblioitem;
-    }
+                push @bibitemloop, $biblioitem;
+            }
 
-    @available_itemtypes = uniq( @available_itemtypes );
-    $template->param( available_itemtypes => \@available_itemtypes );
-
-    # existingreserves building
-    my @reserveloop;
-    my @reserves = Koha::Holds->search( { biblionumber => $biblionumber }, { order_by => 'priority' } );
-    foreach my $res (
-        sort {
-            my $a_found = $a->found() || '';
-            my $b_found = $a->found() || '';
-            $a_found cmp $b_found;
-        } @reserves
-      )
-    {
-        my $priority = $res->priority();
-        my %reserve;
-        my @optionloop;
-        for ( my $i = 1 ; $i <= $totalcount ; $i++ ) {
-            push(
-                @optionloop,
-                {
-                    num      => $i,
-                    selected => ( $i == $priority ),
-                }
+            @available_itemtypes = uniq( @available_itemtypes );
+            $template->param(
+                bibitemloop         => \@bibitemloop,
+                available_itemtypes => \@available_itemtypes
             );
         }
 
-        if ( $res->is_found() ) {
-            $reserve{'holdingbranch'} = $res->item()->holdingbranch();
-            $reserve{'biblionumber'}  = $res->item()->biblionumber();
-            $reserve{'barcodenumber'} = $res->item()->barcode();
-            $reserve{'wbrcode'}       = $res->branchcode();
-            $reserve{'itemnumber'}    = $res->itemnumber();
-            $reserve{'wbrname'}       = $res->branch()->branchname();
-
-            if ( $reserve{'holdingbranch'} eq $reserve{'wbrcode'} ) {
-
-                # Just because the holdingbranch matches the reserve branch doesn't mean the item
-                # has arrived at the destination, check for an open transfer for the item as well
-                my ( $transfertwhen, $transfertfrom, $transferto ) =
-                  C4::Circulation::GetTransfers( $res->itemnumber() );
-                if ( not $transferto or $transferto ne $res->branchcode() ) {
-                    $reserve{'atdestination'} = 1;
+        # existingreserves building
+        my @reserveloop;
+        my $always_show_holds = $input->cookie('always_show_holds');
+        $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' } )->as_list;
+            foreach my $res (
+                sort {
+                    my $a_found = $a->found() || '';
+                    my $b_found = $a->found() || '';
+                    $a_found cmp $b_found;
+                } @reserves
+              )
+            {
+                my %reserve;
+                if ( $res->is_found() ) {
+                    $reserve{'holdingbranch'} = $res->item()->holdingbranch();
+                    $reserve{'biblionumber'}  = $res->item()->biblionumber();
+                    $reserve{'barcodenumber'} = $res->item()->barcode();
+                    $reserve{'wbrcode'}       = $res->branchcode();
+                    $reserve{'itemnumber'}    = $res->itemnumber();
+                    $reserve{'wbrname'}       = $res->branch()->branchname();
+                    $reserve{'atdestination'} = $res->is_at_destination();
+                    $reserve{'desk_name'}     = ( $res->desk() ) ? $res->desk()->desk_name() : '' ;
+                    $reserve{'found'}     = $res->is_found();
+                    $reserve{'inprocessing'} = $res->is_in_processing();
+                    $reserve{'intransit'} = $res->is_in_transit();
+                }
+                elsif ( $res->priority() > 0 ) {
+                    if ( my $item = $res->item() )  {
+                        $reserve{'itemnumber'}      = $item->id();
+                        $reserve{'barcodenumber'}   = $item->barcode();
+                        $reserve{'item_level_hold'} = 1;
+                    }
                 }
-            }
 
-            # set found to 1 if reserve is waiting for patron pickup
-            $reserve{'found'}     = $res->is_found();
-            $reserve{'intransit'} = $res->is_in_transit();
-        }
-        elsif ( $res->priority() > 0 ) {
-            if ( my $item = $res->item() )  {
-                $reserve{'itemnumber'}      = $item->id();
-                $reserve{'barcodenumber'}   = $item->barcode();
-                $reserve{'item_level_hold'} = 1;
+                $reserve{'expirationdate'} = $res->expirationdate;
+                $reserve{'date'}           = $res->reservedate;
+                $reserve{'borrowernumber'} = $res->borrowernumber();
+                $reserve{'biblionumber'}   = $res->biblionumber();
+                $reserve{'patron'}         = $res->borrower;
+                $reserve{'notes'}          = $res->reservenotes();
+                $reserve{'waiting_date'}   = $res->waitingdate();
+                $reserve{'ccode'}          = $res->item() ? $res->item()->ccode() : undef;
+                $reserve{'barcode'}        = $res->item() ? $res->item()->barcode() : undef;
+                $reserve{'priority'}       = $res->priority();
+                $reserve{'lowestPriority'} = $res->lowestPriority();
+                $reserve{'suspend'}        = $res->suspend();
+                $reserve{'suspend_until'}  = $res->suspend_until();
+                $reserve{'reserve_id'}     = $res->reserve_id();
+                $reserve{itemtype}         = $res->itemtype();
+                $reserve{branchcode}       = $res->branchcode();
+                $reserve{non_priority}     = $res->non_priority();
+                $reserve{object}           = $res;
+
+                push( @reserveloop, \%reserve );
             }
         }
 
-        $reserve{'expirationdate'} = output_pref( { dt => dt_from_string( $res->expirationdate ), dateonly => 1 } )
-          unless ( !defined( $res->expirationdate ) || $res->expirationdate eq '0000-00-00' );
-        $reserve{'date'}           = output_pref( { dt => dt_from_string( $res->reservedate ), dateonly => 1 } );
-        $reserve{'borrowernumber'} = $res->borrowernumber();
-        $reserve{'biblionumber'}   = $res->biblionumber();
-        $reserve{'patron'}         = $res->borrower;
-        $reserve{'notes'}          = $res->reservenotes();
-        $reserve{'waiting_date'}   = $res->waitingdate();
-        $reserve{'ccode'}          = $res->item() ? $res->item()->ccode() : undef;
-        $reserve{'barcode'}        = $res->item() ? $res->item()->barcode() : undef;
-        $reserve{'priority'}       = $res->priority();
-        $reserve{'lowestPriority'} = $res->lowestPriority();
-        $reserve{'optionloop'}     = \@optionloop;
-        $reserve{'suspend'}        = $res->suspend();
-        $reserve{'suspend_until'}  = $res->suspend_until();
-        $reserve{'reserve_id'}     = $res->reserve_id();
-        $reserve{itemtype}         = $res->itemtype();
-        $reserve{branchcode}       = $res->branchcode();
-        $reserve{object}           = $res;
-
-        push( @reserveloop, \%reserve );
-    }
+        # get the time for the form name...
+        my $time = time();
 
-    # get the time for the form name...
-    my $time = time();
+        $template->param(
+                         time        => $time,
+                         fixedRank   => $fixedRank,
+                        );
 
-    $template->param(
-                     time        => $time,
-                     fixedRank   => $fixedRank,
-                    );
+        # display infos
+        $template->param(
+                         itemdata_enumchron => $itemdata_enumchron,
+                         itemdata_ccode    => $itemdata_ccode,
+                         date              => $date,
+                         biblionumber      => $biblionumber,
+                         findborrower      => $findborrower,
+                         biblio            => $biblio,
+                         holdsview         => 1,
+                         C4::Search::enabled_staff_search_views,
+                        );
+
+        $biblioloopiter{biblionumber} = $biblionumber;
+        $biblioloopiter{title}  = $biblio->title;
+        $biblioloopiter{author} = $biblio->author;
+        $biblioloopiter{rank} = $fixedRank;
+        $biblioloopiter{reserveloop} = \@reserveloop;
+
+        if (@reserveloop) {
+            $template->param( reserveloop => \@reserveloop );
+        }
 
-    # display infos
-    $template->param(
-                     optionloop        => \@optionloop,
-                     bibitemloop       => \@bibitemloop,
-                     itemdata_enumchron => $itemdata_enumchron,
-                     itemdata_ccode    => $itemdata_ccode,
-                     date              => $date,
-                     biblionumber      => $biblionumber,
-                     findborrower      => $findborrower,
-                     title             => $biblio->title,
-                     author            => $biblio->author,
-                     holdsview => 1,
-                     C4::Search::enabled_staff_search_views,
-                    );
-
-    $biblioloopiter{biblionumber} = $biblionumber;
-    $biblioloopiter{title} = $biblio->title;
-    $biblioloopiter{rank} = $fixedRank;
-    $biblioloopiter{reserveloop} = \@reserveloop;
-
-    if (@reserveloop) {
-        $template->param( reserveloop => \@reserveloop );
+        if ( $patron ) {
+            # Add the valid pickup locations
+            my @pickup_locations = $biblio->pickup_locations({ patron => $patron })->as_list;
+            $biblioloopiter{pickup_locations} = \@pickup_locations;
+            $biblioloopiter{pickup_locations_codes} = [ map { $_->branchcode } @pickup_locations ];
+        }
+
+        push @biblioloop, \%biblioloopiter;
     }
 
-    push @biblioloop, \%biblioloopiter;
+    $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));
+} elsif ( ! $multi_hold ) {
+    my $biblio = Koha::Biblios->find( $biblionumber );
+    $template->param( biblio => $biblio );
+}
+
+if ( $multi_hold ) {
+    $template->param( biblionumbers => join('/', @biblionumbers) );
+} else {
+    $template->param( biblionumber => $biblionumber || $biblionumbers[0] );
 }
 
-$template->param( biblioloop => \@biblioloop );
-$template->param( biblionumbers => $biblionumbers );
-$template->param( exceeded_maxreserves => $exceeded_maxreserves );
-$template->param( exceeded_holds_per_record => $exceeded_holds_per_record );
-$template->param( subscriptionsnumber => CountSubscriptionFromBiblionumber($biblionumber));
-$template->param( pickup => $pickup );
+# pass the userenv branch if no pickup location selected
+$template->param( pickup => $pickup || C4::Context->userenv->{branch} );
 
 if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
     $template->param( reserve_in_future => 1 );