Koha/reserve/request.pl
Nick Clemens 842d448276 Bug 29562: Adjust CanItemBeReserved and checkHighHolds to take objects
Most of the changes here are simple, this can be read to view the changes

Testing that holds can be placed via staff client, and opac, and are disallowed
when expected is the best test plan, beyond running the unit tests

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Fridolin Somers <fridolin.somers@biblibre.com>
2022-01-31 21:55:39 -10:00

817 lines
34 KiB
Perl
Executable file

#!/usr/bin/perl
#written 2/1/00 by chris@katipo.oc.nz
# Copyright 2000-2002 Katipo Communications
# Parts Copyright 2011 Catalyst IT
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
=head1 request.pl
script to place reserves/requests
=cut
use Modern::Perl;
use CGI qw ( -utf8 );
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::Search qw( enabled_staff_search_views );
use Koha::Biblios;
use Koha::DateUtils qw( dt_from_string output_pref );
use Koha::Checkouts;
use Koha::Holds;
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 = CGI->new;
my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user(
{
template_name => "reserve/request.tt",
query => $input,
type => "intranet",
flagsrequired => { reserveforothers => 'place_holds' },
}
);
my $showallitems = $input->param('showallitems');
my $pickup = $input->param('pickup');
my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
# Select borrowers infos
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;
my $exceeded_holds_per_record;
my $date = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
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 $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) {
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'";
}
}
}
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');
if ( $biblionumbers ) {
@biblionumbers = split '/', $biblionumbers;
} else {
push @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) {
my $patron = Koha::Patrons->find( $borrowernumber_hold );
my $diffbranch;
# we check the reserves of the user, and if they can reserve a document
# FIXME At this time we have a simple count of reservs, but, later, we could improve the infos "title" ...
my $reserves_count = $patron->holds->count;
my $new_reserves_count = scalar( @biblionumbers );
my $maxreserves = C4::Context->preference('maxreserves');
$template->param( maxreserves => $maxreserves );
if ( $maxreserves
&& ( $reserves_count + $new_reserves_count > $maxreserves ) )
{
my $new_reserves_allowed =
$maxreserves - $reserves_count > 0
? $maxreserves - $reserves_count
: 0;
$warnings = 1;
$exceeded_maxreserves = 1;
$template->param(
new_reserves_allowed => $new_reserves_allowed,
new_reserves_count => $new_reserves_count,
reserves_count => $reserves_count,
maxreserves => $maxreserves,
);
}
# check if the borrower make the reserv in a different branch
if ( $patron->branchcode ne C4::Context->userenv->{'branch'} ) {
$diffbranch = 1;
}
my $amount_outstanding = $patron->account->balance;
$template->param(
patron => $patron,
diffbranch => $diffbranch,
messages => $messages,
warnings => $warnings,
amount_outstanding => $amount_outstanding,
);
}
if ($club_hold && !$borrowernumber_hold && !$action) {
my $club = Koha::Clubs->find($club_hold);
my $enrollments = $club->club_enrollments;
my $maxreserves = C4::Context->preference('maxreserves');
my $new_reserves_count = scalar( @biblionumbers );
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;
}
$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
);
}
unless ( $club_hold or $borrowernumber_hold ) {
$template->param( clubcount => Koha::Clubs->search->count );
}
$template->param(
messageborrower => $messageborrower,
messageclub => $messageclub
);
# 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 );
}
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 = ();
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 $force_hold_level;
if ( $patron ) {
{ # CanBookBeReserved
my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber );
if ( $canReserve->{status} eq 'OK' ) {
#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;
}
}
# 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 );
}
my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
my $totalcount = $count;
# adding a fixed value for priority options
my $fixedRank = $count+1;
my %itemnumbers_of_biblioitem;
my @hostitems = get_hostitemnumbers_of($biblionumber);
my @itemnumbers;
if (@hostitems){
$template->param('hostitemsflag' => 1);
push(@itemnumbers, @hostitems);
}
my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
unless ( $items->count ) {
# 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;
}
$biblioloopiter{description} = $biblioitem->{description};
$biblioloopiter{itypename} = $biblioitem->{description};
if ( $biblioitem->{itemtype} ) {
$biblioitem->{description} =
$itemtypes->{ $biblioitem->{itemtype} }{description};
$biblioloopiter{imageurl} =
getitemtypeimagelocation( 'intranet',
$itemtypes->{ $biblioitem->{itemtype} }{imageurl} );
}
# 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};
}
$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};
}
if($item->{biblionumber} ne $biblionumber){
$item->{hostitemsflag} = 1;
$item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
}
# 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';
}
# 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;
}
# Management of the notforloan document
if ( $item->{notforloan} ) {
$item->{backgroundcolor} = 'other';
}
# Management of lost or long overdue items
if ( $item->{itemlost} ) {
$item->{backgroundcolor} = 'other';
if ($logged_in_patron->category->hidelostitems && !$showallitems) {
$item->{hide} = 1;
$hiddencount++;
}
}
# Check the transit status
my ( $transfertwhen, $transfertfrom, $transfertto ) =
GetTransfers($itemnumber);
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 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 ( $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 @{ $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;
}
$template->param( hiddencount => $hiddencount);
push @bibitemloop, $biblioitem;
}
@available_itemtypes = uniq( @available_itemtypes );
$template->param(
bibitemloop => \@bibitemloop,
available_itemtypes => \@available_itemtypes
);
}
# 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' } );
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;
}
}
$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 );
}
}
# get the time for the form name...
my $time = time();
$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{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;
}
$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] );
}
# 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 );
}
$template->param(
SuspendHoldsIntranet => C4::Context->preference('SuspendHoldsIntranet'),
AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
);
# printout the page
output_html_with_http_headers $input, $cookie, $template->output;
sub sort_borrowerlist {
my $borrowerslist = shift;
my $ref = [];
push @{$ref}, sort {
uc( $a->{surname} . $a->{firstname} ) cmp
uc( $b->{surname} . $b->{firstname} )
} @{$borrowerslist};
return $ref;
}