X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=opac%2Fopac-reserve.pl;h=1200ef06083e32b34b716271ab466706110f1b60;hb=e1a02dde8f7dbb09172071ebaec1338d58c2634f;hp=e8810650f75955a435105383091b37d202551908;hpb=902aee833968b1273f67bc50a836234e5a0d0940;p=koha-ffzg.git diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl index e8810650f7..1200ef0608 100755 --- a/opac/opac-reserve.pl +++ b/opac/opac-reserve.pl @@ -1,5 +1,6 @@ #!/usr/bin/perl + # Copyright Katipo Communications 2002 # Copyright Koha Development team 2012 # @@ -23,10 +24,9 @@ use Modern::Perl; use CGI qw ( -utf8 ); 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 GetMarcBiblio ); -use C4::Items qw( GetHostItemsInfo GetItemsInfo ); +use C4::Circulation qw( GetBranchItemRule ); +use C4::Reserves qw( CanItemBeReserved CanBookBeReserved AddReserve GetReservesControlBranch ItemsAnyAvailableAndNotRestricted IsAvailableForItemLevelRequest GetReserveFee ); +use C4::Biblio qw( GetBiblioData GetFrameworkCode ); use C4::Output qw( output_html_with_http_headers ); use C4::Context; use C4::Members; @@ -34,7 +34,6 @@ use C4::Overdues; use Koha::AuthorisedValues; use Koha::Biblios; -use Koha::DateUtils qw( dt_from_string output_pref ); use Koha::CirculationRules; use Koha::Items; use Koha::ItemTypes; @@ -61,12 +60,6 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); -my ($show_holds_count, $show_priority); -for ( C4::Context->preference("OPACShowHoldQueueDetails") ) { - m/holds/o and $show_holds_count = 1; - m/priority/ and $show_priority = 1; -} - my $patron = Koha::Patrons->find( $borrowernumber, { prefetch => ['categorycode'] } ); my $category = $patron->category; @@ -79,24 +72,6 @@ unless ( $can_place_hold_if_available_at_pickup ) { } } -# check if this user can place a reserve, -1 means use sys pref, 0 means dont block, 1 means block -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 = $category->reservefee; -if ( $reservefee > 0){ - $template->param( RESERVE_CHARGE => $reservefee); -} - my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } }; # There are two ways of calling this script, with a single biblio num @@ -130,65 +105,77 @@ if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) { exit; } - -# pass the pickup branch along.... -my $branch = $query->param('branch') || $patron->branchcode || C4::Context->userenv->{branch} || '' ; -$template->param( branch => $branch ); - # # -# Build hashes of the requested biblio(item)s and items. +# Here we check that the borrower can actually make reserves Stage 1. # # +my $noreserves = 0; +if ( $category->effective_BlockExpiredPatronOpacActions ) { + if ( $patron->is_expired ) { + # cannot reserve, their card has expired and the rules set mean this is not allowed + $noreserves = 1; + $template->param( message => 1, expired_patron => 1 ); + } +} -my %biblioDataHash; # Hash of biblionumber to biblio/biblioitems record. -my %itemInfoHash; # Hash of itemnumber to item info. -foreach my $biblioNumber (@biblionumbers) { +my $maxoutstanding = C4::Context->preference("maxoutstanding"); +my $amountoutstanding = $patron->account->balance; +if ( $amountoutstanding && ($amountoutstanding > $maxoutstanding) ) { + my $amount = sprintf "%.02f", $amountoutstanding; + $template->param( message => 1 ); + $noreserves = 1; + $template->param( too_much_oweing => $amount ); +} - my $biblioData = GetBiblioData($biblioNumber); - $biblioDataHash{$biblioNumber} = $biblioData; +if ( $patron->gonenoaddress && ($patron->gonenoaddress == 1) ) { + $noreserves = 1; + $template->param( + message => 1, + GNA => 1 + ); +} - my @itemInfos = GetItemsInfo($biblioNumber); +if ( $patron->lost && ($patron->lost == 1) ) { + $noreserves = 1; + $template->param( + message => 1, + lost => 1 + ); +} - my $marcrecord= GetMarcBiblio({ biblionumber => $biblioNumber }); +if ( $patron->is_debarred ) { + $noreserves = 1; + $template->param( + message => 1, + debarred => 1, + debarred_comment => $patron->debarredcomment, + debarred_date => $patron->debarred, + ); +} - # 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); - } +my $holds = $patron->holds; +my $reserves_count = $holds->count; +$template->param( RESERVES => $holds->unblessed ); +if ( $maxreserves && ( $reserves_count >= $maxreserves ) ) { + $template->param( message => 1 ); + $noreserves = 1; + $template->param( too_many_reserves => $holds->count ); +} - $biblioData->{itemInfos} = \@itemInfos; - foreach my $itemInfo (@itemInfos) { - $itemInfoHash{$itemInfo->{itemnumber}} = $itemInfo; - } +if( $noreserves ){ + output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; + exit; +} - # Compute the priority rank. - my $biblio = Koha::Biblios->find( $biblioNumber ); - next unless $biblio; - $biblioData->{object} = $biblio; - my $holds = $biblio->holds; - my $rank = $holds->count; - $biblioData->{reservecount} = 1; # new reserve - while ( my $hold = $holds->next ) { - if ( $hold->is_waiting ) { - $rank--; - } - else { - $biblioData->{reservecount}++; - } - } - $biblioData->{rank} = $rank + 1; -} +# pass the pickup branch along.... +my $branch = $query->param('branch') || $patron->branchcode || C4::Context->userenv->{branch} || '' ; +$template->param( branch => $branch ); # # -# If this is the second time through this script, it -# means we are carrying out the hold request, possibly +# Here we are carrying out the hold request, possibly # with a specific item for each biblionumber. # # @@ -239,6 +226,7 @@ if ( $query->param('place_reserve') ) { $branch = $patron->branchcode; } + # FIXME We shouldn't need to fetch the item here 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") && $item ) { @@ -255,17 +243,11 @@ if ( $query->param('place_reserve') ) { } } -#item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber + # if we have an item, we are placing the hold on the item's bib, in case of analytics if ( $item ) { - my $hostbiblioNum = $item->biblio->biblionumber; - if ( $hostbiblioNum ne $biblioNum ) { - $biblioNum = $hostbiblioNum; - } + $biblioNum = $item->biblionumber; } - my $biblioData = $biblioDataHash{$biblioNum}; - my $found; - # Check for user supplied reserve date my $startdate; if ( C4::Context->preference('AllowHoldDateInFuture') @@ -276,13 +258,17 @@ if ( $query->param('place_reserve') ) { my $patron_expiration_date = $query->param("expiration_date_$biblioNum"); - my $rank = $biblioData->{rank}; - my $patron = Koha::Patrons->find( $borrowernumber ); + my $itemtype = $query->param('itemtype') || undef; + $itemtype = undef if $itemNum; + + my $biblio = Koha::Biblios->find($biblioNum); + my $rank = $biblio->holds->search( { found => [ { "!=" => "W" }, undef ] } )->count + 1; if ( $item ) { $canreserve = 1 if CanItemBeReserved( $patron, $item, $branch )->{status} eq 'OK'; } else { - $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum, $branch )->{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; @@ -304,9 +290,6 @@ 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( @@ -318,9 +301,9 @@ if ( $query->param('place_reserve') ) { reservation_date => $startdate, expiration_date => $patron_expiration_date, notes => $notes, - title => $biblioData->{title}, + title => $biblio->title, itemnumber => $itemNum, - found => $found, + found => undef, itemtype => $itemtype, } ); @@ -335,66 +318,48 @@ if ( $query->param('place_reserve') ) { # # -# Here we check that the borrower can actually make reserves Stage 1. +# Build hashes of the requested biblio(item)s and items. # # -my $noreserves = 0; -my $maxoutstanding = C4::Context->preference("maxoutstanding"); -$template->param( noreserve => 1 ) unless $maxoutstanding; -my $amountoutstanding = $patron->account->balance; -if ( $amountoutstanding && ($amountoutstanding > $maxoutstanding) ) { - my $amount = sprintf "%.02f", $amountoutstanding; - $template->param( message => 1 ); - $noreserves = 1; - $template->param( too_much_oweing => $amount ); -} -if ( $patron->gonenoaddress && ($patron->gonenoaddress == 1) ) { - $noreserves = 1; - $template->param( - message => 1, - GNA => 1 - ); -} +my %biblioDataHash; # Hash of biblionumber to biblio/biblioitems record. +foreach my $biblioNumber (@biblionumbers) { -if ( $patron->lost && ($patron->lost == 1) ) { - $noreserves = 1; - $template->param( - message => 1, - lost => 1 - ); -} + my $biblioData = GetBiblioData($biblioNumber); + $biblioDataHash{$biblioNumber} = $biblioData; -if ( $patron->is_debarred ) { - $noreserves = 1; - $template->param( - message => 1, - debarred => 1, - debarred_comment => $patron->debarredcomment, - debarred_date => $patron->debarred, - ); -} + my $biblio = Koha::Biblios->find( $biblioNumber ); + next unless $biblio; -my $holds = $patron->holds; -my $reserves_count = $holds->count; -$template->param( RESERVES => $holds->unblessed ); -if ( $maxreserves && ( $reserves_count >= $maxreserves ) ) { - $template->param( message => 1 ); - $noreserves = 1; - $template->param( too_many_reserves => $holds->count ); -} + my $items = Koha::Items->search_ordered( + [ + biblionumber => $biblioNumber, + 'me.itemnumber' => { + -in => [ + $biblio->host_items->get_column('itemnumber') + ] + } + ], + { prefetch => [ 'issue', 'homebranch', 'holdingbranch' ] } + )->filter_by_visible_in_opac({ patron => $patron }); -unless ( $noreserves ) { - my $requested_reserves_count = scalar( @biblionumbers ); - if ( $maxreserves && ( $reserves_count + $requested_reserves_count > $maxreserves ) ) { - $template->param( new_reserves_allowed => $maxreserves - $reserves_count ); - } + $biblioData->{items} = [$items->as_list]; # FIXME Potentially a lot in memory here! + + # Compute the priority rank. + $biblioData->{object} = $biblio; + my $reservecount = $biblio->holds->search({ found => [ {"!=" => "W"},undef] })->count; + $biblioData->{reservecount} = $reservecount; + $biblioData->{rank} = $reservecount + 1; } -unless ($noreserves) { - $template->param( select_item_types => 1 ); + +my $requested_reserves_count = scalar( @biblionumbers ); +if ( $maxreserves && ( $reserves_count + $requested_reserves_count > $maxreserves ) ) { + $template->param( new_reserves_allowed => $maxreserves - $reserves_count ); } +$template->param( select_item_types => 1 ); + # # @@ -412,6 +377,7 @@ my $itemLevelTypes = C4::Context->preference('item-level_itypes'); my $pickup_locations = Koha::Libraries->search({ pickup_location => 1 }); $template->param('item_level_itypes' => $itemLevelTypes); +my $patron_unblessed = $patron->unblessed; foreach my $biblioNum (@biblionumbers) { # Init the bib item with the choices for branch pickup @@ -442,32 +408,13 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{rank} = $biblioData->{rank}; $biblioLoopIter{reservecount} = $biblioData->{reservecount}; $biblioLoopIter{already_reserved} = $biblioData->{already_reserved}; - $biblioLoopIter{reqholdnotes}=0; #TODO: For future use if (!$itemLevelTypes && $biblioData->{itemtype}) { $biblioLoopIter{translated_description} = $itemtypes->{$biblioData->{itemtype}}{translated_description}; $biblioLoopIter{imageurl} = getitemtypeimagesrc() . "/". $itemtypes->{$biblioData->{itemtype}}{imageurl}; } - foreach my $itemInfo (@{$biblioData->{itemInfos}}) { - if ($itemLevelTypes && $itemInfo->{itype}) { - $itemInfo->{translated_description} = $itemtypes->{$itemInfo->{itype}}{translated_description}; - $itemInfo->{imageurl} = getitemtypeimagesrc() . "/". $itemtypes->{$itemInfo->{itype}}{imageurl}; - } - - if (!$itemInfo->{'notforloan'} && !($itemInfo->{'itemnotforloan'} > 0)) { - $biblioLoopIter{forloan} = 1; - } - } - - my @notforloan_avs = Koha::AuthorisedValues->search_by_koha_field({ kohafield => 'items.notforloan', frameworkcode => $frameworkcode }); - 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; @@ -478,130 +425,78 @@ foreach my $biblioNum (@biblionumbers) { # (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 = $visible_items->{$itemNum}; - my $itemLoopIter = {}; - - $itemLoopIter->{itemnumber} = $itemNum; - $itemLoopIter->{barcode} = $itemInfo->{barcode}; - $itemLoopIter->{homeBranchName} = $itemInfo->{homebranch}; - $itemLoopIter->{callNumber} = $itemInfo->{itemcallnumber}; - $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}; - $itemLoopIter->{imageurl} = $itemInfo->{imageurl}; - } + foreach my $item (@{$biblioData->{items}}) { - # If the holdingbranch is different than the homebranch, we show the - # holdingbranch of the document too. - if ( $itemInfo->{homebranch} ne $itemInfo->{holdingbranch} ) { - $itemLoopIter->{holdingBranchName} = $itemInfo->{holdingbranch}; - } - - # If the item is currently on loan, we display its return date and - # 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'; + my $item_info = $item->unblessed; + $item_info->{holding_branch} = $item->holding_branch; + $item_info->{home_branch} = $item->home_branch; + if ($itemLevelTypes) { + my $itemtype = $item->itemtype; + $item_info->{'imageurl'} = getitemtypeimagelocation( 'opac', + $itemtypes->{ $itemtype->itemtype }->{'imageurl'} ); + $item_info->{'translated_description'} = + $itemtypes->{ $itemtype->itemtype }->{translated_description}; } - # checking reserve + # checking for holds my $holds = $item->current_holds; - 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->{ExpectedAtLibrary} = $first_hold->branchcode; - $itemLoopIter->{waitingdate} = $first_hold->waitingdate; - } - - $itemLoopIter->{notforloan} = $itemInfo->{notforloan}; - $itemLoopIter->{itemnotforloan} = $itemInfo->{itemnotforloan}; - - # Management of the notforloan document - if ( $itemLoopIter->{notforloan} || $itemLoopIter->{itemnotforloan}) { - $itemLoopIter->{backgroundcolor} = 'other'; - $itemLoopIter->{notforloanvalue} = - $notforloan_label_of->{ $itemLoopIter->{notforloan} }; + $item_info->{first_hold} = $first_hold; } - # Management of lost or long overdue items - if ( $itemInfo->{itemlost} ) { - - # FIXME localized strings should never be in Perl code - $itemLoopIter->{message} = - $itemInfo->{itemlost} == 1 ? "(lost)" - : $itemInfo->{itemlost} == 2 ? "(long overdue)" - : ""; - $itemInfo->{backgroundcolor} = 'other'; - } + $item_info->{checkout} = $item->checkout; # Check of the transferred documents - my ( $transfertwhen, $transfertfrom, $transfertto ) = - GetTransfers($itemNum); - if ( $transfertwhen && ($transfertwhen ne '') ) { - $itemLoopIter->{transfertwhen} = output_pref({ dt => dt_from_string($transfertwhen), dateonly => 1 }); - $itemLoopIter->{transfertfrom} = $transfertfrom; - $itemLoopIter->{transfertto} = $transfertto; - $itemLoopIter->{nocancel} = 1; + my $transfer = $item->get_transfer; + if ( $transfer && $transfer->in_transit ) { + $item_info->{transfertwhen} = $transfer->datesent; + $item_info->{transfertfrom} = $transfer->frombranch; + $item_info->{transfertto} = $transfer->tobranch; + $item_info->{nocancel} = 1; } # 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; + if ( $item_info->{biblionumber} ne $biblioNum ) { + $item_info->{hostbiblionumber} = $item->biblionumber; + $item_info->{hosttitle} = Koha::Biblios->find( $item_info->{biblionumber} )->title; } - # If there is no loan, return and transfer, we show a checkbox. - $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0; + my $branch = GetReservesControlBranch( $item_info, $patron_unblessed ); - my $patron_unblessed = $patron->unblessed; - my $branch = GetReservesControlBranch( $itemInfo, $patron_unblessed ); - - my $policy_holdallowed = !$itemLoopIter->{already_reserved}; # items_any_available defined outside of the current loop, # so we avoiding loop inside IsAvailableForItemLevelRequest: - $policy_holdallowed &&= + my $policy_holdallowed = CanItemBeReserved( $patron, $item )->{status} eq 'OK' && IsAvailableForItemLevelRequest($item, $patron, undef, $items_any_available); if ($policy_holdallowed) { 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; + $item_info->{available} = 1; $numCopiesOPACAvailable++; $biblioLoopIter{force_hold} = 1 if $opac_hold_policy eq 'F'; } $numCopiesAvailable++; unless ( $can_place_hold_if_available_at_pickup ) { - my $items_in_this_library = Koha::Items->search({ biblionumber => $itemInfo->{biblionumber}, holdingbranch => $itemInfo->{holdingbranch} }); + my $items_in_this_library = Koha::Items->search({ biblionumber => $item->biblionumber, holdingbranch => $item->holdingbranch }); my $nb_of_items_issued = $items_in_this_library->search({ 'issue.itemnumber' => { not => undef }}, { join => 'issue' })->count; if ( $items_in_this_library->count > $nb_of_items_issued ) { - push @not_available_at, $itemInfo->{holdingbranch}; + push @not_available_at, $item->holdingbranch; } } } - $itemLoopIter->{imageurl} = getitemtypeimagelocation( 'opac', $itemtypes->{ $itemInfo->{itype} }{imageurl} ); - - # Show serial enumeration when needed - if ($itemLoopIter->{enumchron}) { + # Show serial enumeration when needed + if ($item_info->{enumchron}) { $itemdata_enumchron = 1; } - # Show collection when needed - if ($itemLoopIter->{ccode}) { + # Show collection when needed + if ($item_info->{ccode}) { $itemdata_ccode = 1; } - push @{$biblioLoopIter{itemLoop}}, $itemLoopIter; + push @{$biblioLoopIter{itemLoop}}, $item_info; } $template->param( itemdata_enumchron => $itemdata_enumchron, @@ -634,7 +529,27 @@ foreach my $biblioNum (@biblionumbers) { my $status = CanBookBeReserved( $borrowernumber, $biblioNum )->{status}; $biblioLoopIter{holdable} &&= $status eq 'OK'; - $biblioLoopIter{already_patron_possession} = $status eq 'alreadypossession'; + $biblioLoopIter{$status} = 1; + + if ( $biblioLoopIter{holdable} and C4::Context->preference('AllowHoldItemTypeSelection') ) { + # build the allowed item types loop + my $rs = $biblio->items->search_ordered( + 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 @@ -654,6 +569,8 @@ foreach my $biblioNum (@biblionumbers) { $biblioLoopIter{forced_hold_level} = $forced_hold_level; } + # Pass through any reserve charge + $biblioLoopIter{reserve_charge} = GetReserveFee( $patron->id, $biblioNum ); push @$biblioLoop, \%biblioLoopIter;