X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=opac%2Fopac-reserve.pl;h=a4cacfe50fcfaa5d5f1af26c6a2827d4a471fced;hb=e41cde66f011abdc1a7476ea6842ff04996975d2;hp=eb017b64854703a9c255da5f8328811819f81d52;hpb=6a0246e24838cfba672baf4aef080588b7d091bf;p=koha-ffzg.git diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl index eb017b6485..a4cacfe50f 100755 --- a/opac/opac-reserve.pl +++ b/opac/opac-reserve.pl @@ -21,36 +21,34 @@ use Modern::Perl; use CGI qw ( -utf8 ); -use C4::Auth; # checkauth, getborrowernumber. -use C4::Koha; -use C4::Circulation; -use C4::Reserves; -use C4::Biblio; -use C4::Items; -use C4::Output; +use C4::Auth qw( get_template_and_user ); +use C4::Koha qw( getitemtypeimagelocation getitemtypeimagesrc ); +use C4::Circulation qw( GetBranchItemRule GetTransfers ); +use C4::Reserves qw( CanItemBeReserved CanBookBeReserved AddReserve GetReservesControlBranch ItemsAnyAvailableAndNotRestricted IsAvailableForItemLevelRequest ); +use C4::Biblio qw( GetBiblioData GetFrameworkCode ); +use C4::Items qw( GetHostItemsInfo GetItemsInfo ); +use C4::Output qw( output_html_with_http_headers ); use C4::Context; use C4::Members; use C4::Overdues; -use C4::Debug; use Koha::AuthorisedValues; use Koha::Biblios; -use Koha::DateUtils; -use Koha::IssuingRules; +use Koha::DateUtils qw( dt_from_string output_pref ); +use Koha::CirculationRules; use Koha::Items; use Koha::ItemTypes; use Koha::Checkouts; use Koha::Libraries; use Koha::Patrons; -use Date::Calc qw/Today Date_to_Days/; -use List::MoreUtils qw/uniq/; +use List::MoreUtils qw( uniq ); my $maxreserves = C4::Context->preference("maxreserves"); -my $query = new CGI; +my $query = CGI->new; -# if RequestOnOpac (for placing holds) is disabled, leave immediately -if ( ! C4::Context->preference('RequestOnOpac') ) { +# if OPACHoldRequests (for placing holds) is disabled, leave immediately +if ( ! C4::Context->preference('OPACHoldRequests') ) { print $query->redirect("/cgi-bin/koha/errors/404.pl"); exit; } @@ -60,8 +58,6 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( template_name => "opac-reserve.tt", query => $query, type => "opac", - authnotrequired => 0, - debug => 1, } ); @@ -71,30 +67,32 @@ for ( C4::Context->preference("OPACShowHoldQueueDetails") ) { m/priority/ and $show_priority = 1; } -my $patron = Koha::Patrons->find( $borrowernumber ); +my $patron = Koha::Patrons->find( $borrowernumber, { prefetch => ['categorycode'] } ); +my $category = $patron->category; my $can_place_hold_if_available_at_pickup = C4::Context->preference('OPACHoldsIfAvailableAtPickup'); unless ( $can_place_hold_if_available_at_pickup ) { - my @patron_categories = split '\|', C4::Context->preference('OPACHoldsIfAvailableAtPickupExceptions'); + my @patron_categories = split ',', C4::Context->preference('OPACHoldsIfAvailableAtPickupExceptions'); if ( @patron_categories ) { my $categorycode = $patron->categorycode; - $can_place_hold_if_available_at_pickup = grep /^$categorycode$/, @patron_categories; + $can_place_hold_if_available_at_pickup = grep { $_ eq $categorycode } @patron_categories; } } # check if this user can place a reserve, -1 means use sys pref, 0 means dont block, 1 means block -if ( $patron->category->effective_BlockExpiredPatronOpacActions ) { +if ( $category->effective_BlockExpiredPatronOpacActions ) { if ( $patron->is_expired ) { # cannot reserve, their card has expired and the rules set mean this is not allowed $template->param( message => 1, expired_patron => 1 ); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; } } # Pass through any reserve charge -my $reservefee = $patron->category->reservefee; +my $reservefee = $category->reservefee; if ( $reservefee > 0){ $template->param( RESERVE_CHARGE => $reservefee); } @@ -116,6 +114,7 @@ if (! $biblionumbers) { if ((! $biblionumbers) && (! $query->param('place_reserve'))) { $template->param(message=>1, no_biblionumber=>1); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; } # Pass the numbers to the page so they can be fed back @@ -128,6 +127,7 @@ if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) { # TODO: New message? $template->param(message=>1, no_biblionumber=>1); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; } @@ -150,14 +150,15 @@ foreach my $biblioNumber (@biblionumbers) { my @itemInfos = GetItemsInfo($biblioNumber); - my $marcrecord= GetMarcBiblio({ biblionumber => $biblioNumber }); + my $biblio = Koha::Biblios->find( $biblioNumber ); + next unless $biblio; + + my $marcrecord = $biblio->metadata->record; # flag indicating existence of at least one item linked via a host record - my $hostitemsflag; # adding items linked via host biblios my @hostitemInfos = GetHostItemsInfo($marcrecord); if (@hostitemInfos){ - $hostitemsflag =1; push (@itemInfos,@hostitemInfos); } @@ -167,7 +168,6 @@ foreach my $biblioNumber (@biblionumbers) { } # Compute the priority rank. - my $biblio = Koha::Biblios->find( $biblioNumber ); $biblioData->{object} = $biblio; my $holds = $biblio->holds; my $rank = $holds->count; @@ -220,6 +220,7 @@ if ( $query->param('place_reserve') ) { if (($selectionCount == 0) || (($selectionCount % 3) != 0)) { $template->param(message=>1, bad_data=>1); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; } my $failed_holds = 0; @@ -236,14 +237,16 @@ if ( $query->param('place_reserve') ) { $branch = $patron->branchcode; } + my $item = $itemNum ? Koha::Items->find( $itemNum ) : undef; # When choosing a specific item, the default pickup library should be dictated by the default hold policy - if ( ! C4::Context->preference("OPACAllowUserToChooseBranch") && $itemNum ) { - my $item = Koha::Items->find( $itemNum ); + if ( ! C4::Context->preference("OPACAllowUserToChooseBranch") && $item ) { my $type = $item->effective_itemtype; my $rule = GetBranchItemRule( $patron->branchcode, $type ); - if ( $rule->{hold_fulfillment_policy} eq 'any' ) { + if ( $rule->{hold_fulfillment_policy} eq 'any' || $rule->{hold_fulfillment_policy} eq 'patrongroup' ) { $branch = $patron->branchcode; + } elsif ( $rule->{hold_fulfillment_policy} eq 'holdgroup' ){ + $branch = $item->homebranch; } else { my $policy = $rule->{hold_fulfillment_policy}; $branch = $item->$policy; @@ -251,8 +254,7 @@ if ( $query->param('place_reserve') ) { } #item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber - if ( $itemNum ne '' ) { - my $item = Koha::Items->find( $itemNum ); + if ( $item ) { my $hostbiblioNum = $item->biblio->biblionumber; if ( $hostbiblioNum ne $biblioNum ) { $biblioNum = $hostbiblioNum; @@ -270,14 +272,19 @@ if ( $query->param('place_reserve') ) { $startdate = $query->param("reserve_date_$biblioNum"); } - my $expiration_date = $query->param("expiration_date_$biblioNum"); + my $patron_expiration_date = $query->param("expiration_date_$biblioNum"); + + my $itemtype = $query->param('itemtype') || undef; + $itemtype = undef if $itemNum; my $rank = $biblioData->{rank}; - if ( $itemNum ne '' ) { - $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum )->{status} eq 'OK'; + my $patron = Koha::Patrons->find( $borrowernumber ); + if ( $item ) { + $canreserve = 1 if CanItemBeReserved( $patron, $item, $branch )->{status} eq 'OK'; } else { - $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum )->{status} eq 'OK'; + $canreserve = 1 + if CanBookBeReserved( $borrowernumber, $biblioNum, $branch, { itemtype => $itemtype } )->{status} eq 'OK'; # Inserts a null into the 'itemnumber' field of 'reserves' table. $itemNum = undef; @@ -299,16 +306,22 @@ if ( $query->param('place_reserve') ) { } } - my $itemtype = $query->param('itemtype') || undef; - $itemtype = undef if $itemNum; - # Here we actually do the reserveration. Stage 3. if ($canreserve) { my $reserve_id = AddReserve( - $branch, $borrowernumber, $biblioNum, - [$biblioNum], $rank, $startdate, - $expiration_date, $notes, $biblioData->{title}, - $itemNum, $found, $itemtype, + { + branchcode => $branch, + borrowernumber => $borrowernumber, + biblionumber => $biblioNum, + priority => $rank, + reservation_date => $startdate, + expiration_date => $patron_expiration_date, + notes => $notes, + title => $biblioData->{title}, + itemnumber => $itemNum, + found => $found, + itemtype => $itemtype, + } ); $failed_holds++ unless $reserve_id; ++$reserve_cnt; @@ -400,7 +413,6 @@ $template->param('item_level_itypes' => $itemLevelTypes); foreach my $biblioNum (@biblionumbers) { - my $record = GetMarcBiblio({ biblionumber => $biblioNum }); # Init the bib item with the choices for branch pickup my %biblioLoopIter; @@ -409,6 +421,7 @@ foreach my $biblioNum (@biblionumbers) { if (! $biblioData) { $template->param(message=>1, bad_biblionumber=>$biblioNum); output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; } my @not_available_at = (); @@ -420,7 +433,10 @@ foreach my $biblioNum (@biblionumbers) { my $frameworkcode = GetFrameworkCode( $biblioData->{biblionumber} ); $biblioLoopIter{biblionumber} = $biblioData->{biblionumber}; $biblioLoopIter{title} = $biblioData->{title}; - $biblioLoopIter{subtitle} = GetRecordValue('subtitle', $record, $frameworkcode); + $biblioLoopIter{subtitle} = $biblioData->{'subtitle'}; + $biblioLoopIter{medium} = $biblioData->{medium}; + $biblioLoopIter{part_number} = $biblioData->{part_number}; + $biblioLoopIter{part_name} = $biblioData->{part_name}; $biblioLoopIter{author} = $biblioData->{author}; $biblioLoopIter{rank} = $biblioData->{rank}; $biblioLoopIter{reservecount} = $biblioData->{reservecount}; @@ -443,15 +459,27 @@ foreach my $biblioNum (@biblionumbers) { } } - my @notforloan_avs = Koha::AuthorisedValues->search_by_koha_field({ kohafield => 'items.notforloan', frameworkcode => $frameworkcode }); + my @notforloan_avs = Koha::AuthorisedValues->search_by_koha_field({ kohafield => 'items.notforloan', frameworkcode => $frameworkcode })->as_list; my $notforloan_label_of = { map { $_->authorised_value => $_->opac_description } @notforloan_avs }; + my $visible_items = { map { $_->itemnumber => $_ } $biblio->items->filter_by_visible_in_opac( { patron => $patron } )->as_list }; + + # Only keep the items that are visible in the opac (i.e. those in %visible_items) + # FIXME: We should get rid of itemInfos altogether and use $visible_items + $biblioData->{itemInfos} = [ grep { $visible_items->{ $_->{itemnumber} } } @{ $biblioData->{itemInfos} } ]; + $biblioLoopIter{itemLoop} = []; my $numCopiesAvailable = 0; my $numCopiesOPACAvailable = 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 => $biblioNum, patron => $patron }) if $patron; foreach my $itemInfo (@{$biblioData->{itemInfos}}) { my $itemNum = $itemInfo->{itemnumber}; - my $item = Koha::Items->find( $itemNum ); + my $item = $visible_items->{$itemNum}; my $itemLoopIter = {}; $itemLoopIter->{itemnumber} = $itemNum; @@ -461,6 +489,7 @@ foreach my $biblioNum (@biblionumbers) { $itemLoopIter->{enumchron} = $itemInfo->{enumchron}; $itemLoopIter->{ccode} = $itemInfo->{ccode}; $itemLoopIter->{copynumber} = $itemInfo->{copynumber}; + $itemLoopIter->{itemnotes} = $itemInfo->{itemnotes}; if ($itemLevelTypes) { $itemLoopIter->{translated_description} = $itemInfo->{translated_description}; $itemLoopIter->{itype} = $itemInfo->{itype}; @@ -477,8 +506,8 @@ foreach my $biblioNum (@biblionumbers) { # change the background color. my $issue = Koha::Checkouts->find( { itemnumber => $itemNum } ); if ( $issue ) { - $itemLoopIter->{dateDue} = output_pref({ dt => dt_from_string($issue->date_due, 'sql'), as_due_date => 1 }); - $itemLoopIter->{backgroundcolor} = 'onloan'; + $itemLoopIter->{dateDue} = $issue->date_due; + $itemLoopIter->{onloan} = 'onloan'; } # checking reserve @@ -486,7 +515,7 @@ foreach my $biblioNum (@biblionumbers) { if ( my $first_hold = $holds->next ) { $itemLoopIter->{backgroundcolor} = 'reserved'; - $itemLoopIter->{reservedate} = output_pref({ dt => dt_from_string($first_hold->reservedate), dateonly => 1 }); # FIXME Should be formatted in the template + $itemLoopIter->{reservedate} = $first_hold->reservedate; $itemLoopIter->{ExpectedAtLibrary} = $first_hold->branchcode; $itemLoopIter->{waitingdate} = $first_hold->waitingdate; } @@ -524,7 +553,6 @@ foreach my $biblioNum (@biblionumbers) { # if the items belongs to a host record, show link to host record if ( $itemInfo->{biblionumber} ne $biblioNum ) { - $biblioLoopIter{hostitemsflag} = 1; $itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber}; $itemLoopIter->{hosttitle} = Koha::Biblios->find( $itemInfo->{biblionumber} )->title; } @@ -535,13 +563,14 @@ foreach my $biblioNum (@biblionumbers) { my $patron_unblessed = $patron->unblessed; my $branch = GetReservesControlBranch( $itemInfo, $patron_unblessed ); - my $policy_holdallowed = !$itemLoopIter->{already_reserved}; - $policy_holdallowed &&= - IsAvailableForItemLevelRequest($itemInfo,$patron_unblessed) && - CanItemBeReserved( $borrowernumber, $itemNum )->{status} eq 'OK'; + # items_any_available defined outside of the current loop, + # so we avoiding loop inside IsAvailableForItemLevelRequest: + my $policy_holdallowed = + CanItemBeReserved( $patron, $item )->{status} eq 'OK' && + IsAvailableForItemLevelRequest($item, $patron, undef, $items_any_available); if ($policy_holdallowed) { - my $opac_hold_policy = Koha::IssuingRules->get_opacitemholds_policy( { item => $item, patron => $patron } ); + my $opac_hold_policy = Koha::CirculationRules->get_opacitemholds_policy( { item => $item, patron => $patron } ); if ( $opac_hold_policy ne 'N' ) { # If Y or F $itemLoopIter->{available} = 1; $numCopiesOPACAvailable++; @@ -586,11 +615,6 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{holdable} = undef; $biblioLoopIter{itemholdable} = undef; } - if(not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowernumber,$biblioNum)) { - $biblioLoopIter{holdable} = undef; - $biblioLoopIter{already_patron_possession} = 1; - } - if ( $biblioLoopIter{holdable} ) { @not_available_at = uniq @not_available_at; $biblioLoopIter{not_available_at} = \@not_available_at ; @@ -605,7 +629,29 @@ foreach my $biblioNum (@biblionumbers) { } } - $biblioLoopIter{holdable} &&= CanBookBeReserved( $borrowernumber, $biblioNum )->{status} eq 'OK'; + my $status = CanBookBeReserved( $borrowernumber, $biblioNum )->{status}; + $biblioLoopIter{holdable} &&= $status eq 'OK'; + $biblioLoopIter{already_patron_possession} = $status eq 'alreadypossession'; + + if ( $biblioLoopIter{holdable} and C4::Context->preference('AllowHoldItemTypeSelection') ) { + # build the allowed item types loop + my $rs = $biblio->items->search( + undef, + { select => [ { distinct => 'itype' } ], + as => 'item_type' + } + ); + + my @item_types = + grep { CanBookBeReserved( $borrowernumber, $biblioNum, $branch, { itemtype => $_ } )->{status} eq 'OK' } + $rs->get_column('item_type'); + + $biblioLoopIter{allowed_item_types} = \@item_types; + } + + if ( $status eq 'recall' ) { + $biblioLoopIter{recall} = 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 @@ -622,6 +668,7 @@ foreach my $biblioNum (@biblionumbers) { if ($forced_hold_level) { $biblioLoopIter{force_hold} = 1 if $forced_hold_level eq 'item'; $biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record'; + $biblioLoopIter{forced_hold_level} = $forced_hold_level; }