Bug 10584 - Hide OPAC biblio details if all items are hidden
[koha.git] / opac / opac-reserve.pl
index 7719ab3..4bba9a6 100755 (executable)
@@ -1,5 +1,8 @@
 #!/usr/bin/perl
 
+# Copyright Katipo Communications 2002
+# Copyright Koha Development team 2012
+#
 # This file is part of Koha.
 #
 # Koha is free software; you can redistribute it and/or modify it under the
 # 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, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 use strict;
 use warnings;
 use CGI;
-use C4::Biblio;
-use C4::Items;
 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::Dates qw/format_date/;
 use C4::Context;
 use C4::Members;
 use C4::Branch; # GetBranches
+use C4::Overdues;
 use C4::Debug;
+use Koha::DateUtils;
 # use Data::Dumper;
 
 my $MAXIMUM_NUMBER_OF_RESERVES = C4::Context->preference("maxreserves");
@@ -46,7 +51,13 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
     }
 );
 
-sub get_out ($$$) {
+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;
+}
+
+sub get_out {
        output_html_with_http_headers(shift,shift,shift); # $query, $cookie, $template->output;
        exit;
 }
@@ -54,6 +65,10 @@ sub get_out ($$$) {
 # get borrower information ....
 my ( $borr ) = GetMemberDetails( $borrowernumber );
 
+# Pass through any reserve charge
+if ($borr->{reservefee} > 0){
+    $template->param( RESERVE_CHARGE => sprintf("%.2f",$borr->{reservefee}));
+}
 # get branches and itemtypes
 my $branches = GetBranches();
 my $itemTypes = GetItemTypes();
@@ -88,48 +103,17 @@ if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) {
 }
 
 # pass the pickup branch along....
-my $branch = $query->param('branch');
+my $branch = $query->param('branch') || $borr->{'branchcode'} || C4::Context->userenv->{branch} || '' ;
+($branches->{$branch}) or $branch = "";     # Confirm branch is real
 $template->param( branch => $branch );
 
-# make sure it's a real branch
-if ( !$branch || !$branches->{$branch} ) {
-    $branch = '';
-}
-$template->param( branchname => $branches->{$branch}->{'branchname'} );
-
 # make branch selection options...
-my @branches;
-my @select_branch;
-my %select_branches;
-
-my @CGIbranchlooparray;
-
-foreach my $branch ( keys %$branches ) {
-    if ($branch) {
-        my %line;
-        $line{branch} = $branches->{$branch}->{'branchname'};
-        $line{value}  = $branch;
-        if ($line{value} eq C4::Context->userenv->{'branch'}) {
-            $line{selected} = 1;
-        }
-        push @CGIbranchlooparray, \%line;
-    }
-}
-@CGIbranchlooparray =
-   sort { $a->{branch} cmp $b->{branch} } @CGIbranchlooparray;
-my $CGIbranchloop = \@CGIbranchlooparray;
-$template->param( CGIbranch => $CGIbranchloop );
-
-
-#Debug
-#output_html_with_http_headers($query,$cookie,"<html><head></head><body> @biblionumbers </body></html>\n");
-#exit;
-#my %bibdata;
-#my $rank;
-#my $biblionumber;
-#my $bibdata;
-#my %itemhash;
-#my $forloan;
+my $branchloop = GetBranchesLoop($branch);
+
+# Is the person allowed to choose their branch
+my $OPACChooseBranch = (C4::Context->preference("OPACAllowUserToChooseBranch")) ? 1 : 0;
+
+$template->param( choose_branch => $OPACChooseBranch);
 
 #
 #
@@ -137,34 +121,45 @@ $template->param( CGIbranch => $CGIbranchloop );
 #
 #
 
-# Hash of biblionumber to biblio/biblioitems record.
-my %biblioDataHash;
-
-# Hash of itemnumber to item info.
-my %itemInfoHash;
-
+my %biblioDataHash; # Hash of biblionumber to biblio/biblioitems record.
+my %itemInfoHash; # Hash of itemnumber to item info.
 foreach my $biblioNumber (@biblionumbers) {
 
     my $biblioData = GetBiblioData($biblioNumber);
     $biblioDataHash{$biblioNumber} = $biblioData;
 
     my @itemInfos = GetItemsInfo($biblioNumber);
+
+    my $marcrecord= GetMarcBiblio($biblioNumber);
+
+    # 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);
+    }
+
     $biblioData->{itemInfos} = \@itemInfos;
     foreach my $itemInfo (@itemInfos) {
         $itemInfoHash{$itemInfo->{itemnumber}} = $itemInfo;
     }
 
     # Compute the priority rank.
-    my ( $rank, $reserves ) = GetReservesFromBiblionumber($biblioNumber);
-    $biblioData->{reservecount} = $rank;
-    foreach my $res (@$reserves) {
-        my $found = $res->{'found'};
-        if ( $found && ($found eq 'W') ) {
+    my ( $rank, $reserves ) =
+      GetReservesFromBiblionumber( $biblioNumber, 1 );
+    $biblioData->{reservecount} = 1;    # new reserve
+    foreach my $res (@{$reserves}) {
+        my $found = $res->{found};
+        if ( $found && $found eq 'W' ) {
             $rank--;
         }
+        else {
+            $biblioData->{reservecount}++;
+        }
     }
-    $rank++;
-    $biblioData->{rank} = $rank;
+    $biblioData->{rank} = $rank + 1;
 }
 
 #
@@ -175,8 +170,10 @@ foreach my $biblioNumber (@biblionumbers) {
 #
 #
 if ( $query->param('place_reserve') ) {
-
-    my $notes = $query->param('notes');
+    my $reserve_cnt = 0;
+    if ($MAXIMUM_NUMBER_OF_RESERVES) {
+        $reserve_cnt = GetReservesFromBorrowernumber( $borrowernumber );
+    }
 
     # List is composed of alternating biblio/item/branch
     my $selectedItems = $query->param('selecteditems');
@@ -192,8 +189,9 @@ if ( $query->param('place_reserve') ) {
         my $branch = $query->param('branch');
         $selectedItems = "$bib/$item/$branch/";
     }
-    
-    my @selectedItems = split /\//, $selectedItems;
+
+    $selectedItems =~ s!/$!!;
+    my @selectedItems = split /\//, $selectedItems, -1;
 
     # Make sure there is a biblionum/itemnum/branch triplet for each item.
     # The itemnum can be 'any', meaning next available.
@@ -205,35 +203,76 @@ if ( $query->param('place_reserve') ) {
 
     while (@selectedItems) {
         my $biblioNum = shift(@selectedItems);
-        my $itemNum = shift(@selectedItems);
-        my $branch = shift(@selectedItems); # i.e., branch code, not name
+        my $itemNum   = shift(@selectedItems);
+        my $branch    = shift(@selectedItems);    # i.e., branch code, not name
+
+        my $canreserve = 0;
 
-        my $singleBranchMode = $template->param('singleBranchMode');
-        if ($singleBranchMode) {
+        my $singleBranchMode = C4::Context->preference("singleBranchMode");
+        if ( $singleBranchMode || !$OPACChooseBranch )
+        {    # single branch mode or disabled user choosing
             $branch = $borr->{'branchcode'};
         }
 
+#item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber
+        if ( $itemNum ne '' ) {
+            my $hostbiblioNum = GetBiblionumberFromItemnumber($itemNum);
+            if ( $hostbiblioNum ne $biblioNum ) {
+                $biblioNum = $hostbiblioNum;
+            }
+        }
+
         my $biblioData = $biblioDataHash{$biblioNum};
         my $found;
-        
-        # If a specific item was selected and the pickup branch is the same as the
-        # holdingbranch, force the value $rank and $found.
+
+        # Check for user supplied reserve date
+        my $startdate;
+        if (   C4::Context->preference('AllowHoldDateInFuture')
+            && C4::Context->preference('OPACAllowHoldDateInFuture') )
+        {
+            $startdate = $query->param("reserve_date_$biblioNum");
+        }
+
+        my $expiration_date = $query->param("expiration_date_$biblioNum");
+
+      # If a specific item was selected and the pickup branch is the same as the
+      # holdingbranch, force the value $rank and $found.
         my $rank = $biblioData->{rank};
-        if ($itemNum ne ''){
+        if ( $itemNum ne '' ) {
+            $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum );
             $rank = '0' unless C4::Context->preference('ReservesNeedReturns');
             my $item = GetItem($itemNum);
-            if ( $item->{'holdingbranch'} eq $branch ){
-                $found = 'W' unless C4::Context->preference('ReservesNeedReturns');
+            if ( $item->{'holdingbranch'} eq $branch ) {
+                $found = 'W'
+                  unless C4::Context->preference('ReservesNeedReturns');
             }
         }
         else {
+            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum );
+
             # Inserts a null into the 'itemnumber' field of 'reserves' table.
             $itemNum = undef;
         }
-        
+        my $notes = $query->param('notes_'.$biblioNum)||'';
+
+        if (   $MAXIMUM_NUMBER_OF_RESERVES
+            && $reserve_cnt >= $MAXIMUM_NUMBER_OF_RESERVES )
+        {
+            $canreserve = 0;
+        }
+
         # Here we actually do the reserveration. Stage 3.
-        AddReserve($branch, $borrowernumber, $biblioNum, 'a', [$biblioNum], $rank, $notes,
-                   $biblioData->{'title'}, $itemNum, $found);
+        if ($canreserve) {
+            AddReserve(
+                $branch,      $borrowernumber,
+                $biblioNum,   'a',
+                [$biblioNum], $rank,
+                $startdate,   $expiration_date,
+                $notes,       $biblioData->{title},
+                $itemNum,     $found
+            );
+            ++$reserve_cnt;
+        }
     }
 
     print $query->redirect("/cgi-bin/koha/opac-user.pl#opac-user-holds");
@@ -254,21 +293,21 @@ if ( $borr->{'amountoutstanding'} && ($borr->{'amountoutstanding'} > $maxoutstan
     $noreserves = 1;
     $template->param( too_much_oweing => $amount );
 }
-if ( $borr->{gonenoaddress} && ($borr->{gonenoaddress} eq 1) ) {
+if ( $borr->{gonenoaddress} && ($borr->{gonenoaddress} == 1) ) {
     $noreserves = 1;
     $template->param(
                      message => 1,
                      GNA     => 1
                     );
 }
-if ( $borr->{lost} && ($borr->{lost} eq 1) ) {
+if ( $borr->{lost} && ($borr->{lost} == 1) ) {
     $noreserves = 1;
     $template->param(
                      message => 1,
                      lost    => 1
                     );
 }
-if ( $borr->{debarred} && ($borr->{debarred} eq 1) ) {
+if ( CheckBorrowerDebarred($borrowernumber) ) {
     $noreserves = 1;
     $template->param(
                      message  => 1,
@@ -278,7 +317,7 @@ if ( $borr->{debarred} && ($borr->{debarred} eq 1) ) {
 
 my @reserves = GetReservesFromBorrowernumber( $borrowernumber );
 $template->param( RESERVES => \@reserves );
-if ( scalar(@reserves) >= $MAXIMUM_NUMBER_OF_RESERVES ) {
+if ( $MAXIMUM_NUMBER_OF_RESERVES && (scalar(@reserves) >= $MAXIMUM_NUMBER_OF_RESERVES) ) {
     $template->param( message => 1 );
     $noreserves = 1;
     $template->param( too_many_reserves => scalar(@reserves));
@@ -309,28 +348,32 @@ my $notforloan_label_of = get_notforloan_label_of();
 
 my $biblioLoop = [];
 my $numBibsAvailable = 0;
+my $itemdata_enumchron = 0;
+my $anyholdable = 0;
 my $itemLevelTypes = C4::Context->preference('item-level_itypes');
-$template->param('item-level_itypes' => $itemLevelTypes);
+$template->param('item_level_itypes' => $itemLevelTypes);
 
 foreach my $biblioNum (@biblionumbers) {
 
-    
+    my $record = GetMarcBiblio($biblioNum);
     # Init the bib item with the choices for branch pickup
-    my %biblioLoopIter = ( branchChoicesLoop => $CGIbranchloop );
+    my %biblioLoopIter = ( branchloop => $branchloop );
 
     # Get relevant biblio data.
     my $biblioData = $biblioDataHash{$biblioNum};
     if (! $biblioData) {
         $template->param(message=>1, bad_biblionumber=>$biblioNum);
-       &get_out($query, $cookie, $template->output);
+        &get_out($query, $cookie, $template->output);
     }
 
     $biblioLoopIter{biblionumber} = $biblioData->{biblionumber};
     $biblioLoopIter{title} = $biblioData->{title};
+    $biblioLoopIter{subtitle} = GetRecordValue('subtitle', $record, GetFrameworkCode($biblioData->{biblionumber}));
     $biblioLoopIter{author} = $biblioData->{author};
     $biblioLoopIter{rank} = $biblioData->{rank};
     $biblioLoopIter{reservecount} = $biblioData->{reservecount};
     $biblioLoopIter{already_reserved} = $biblioData->{already_reserved};
+    $biblioLoopIter{mandatorynotes}=0; #FIXME: For future use
 
     if (!$itemLevelTypes && $biblioData->{itemtype}) {
         $biblioLoopIter{description} = $itemTypes->{$biblioData->{itemtype}}{description};
@@ -344,21 +387,17 @@ foreach my $biblioNum (@biblionumbers) {
         my $fee = GetReserveFee(undef, $borrowernumber, $itemInfo->{'biblionumber'}, 'a',
                                 ( $itemInfo->{'biblioitemnumber'} ) );
         $itemInfo->{'reservefee'} = sprintf "%.02f", ($fee ? $fee : 0.0);
-        
+
         if ($itemLevelTypes && $itemInfo->{itype}) {
             $itemInfo->{description} = $itemTypes->{$itemInfo->{itype}}{description};
             $itemInfo->{imageurl} = getitemtypeimagesrc() . "/". $itemTypes->{$itemInfo->{itype}}{imageurl};
         }
-        
+
         if (!$itemInfo->{'notforloan'} && !($itemInfo->{'itemnotforloan'} > 0)) {
             $biblioLoopIter{forloan} = 1;
-       }
+        }
     }
 
-    $biblioLoopIter{itemTypeDescription} =
-      $itemTypes->{$biblioData->{itemtype}}{description};
-    
-
     $biblioLoopIter{itemLoop} = [];
     my $numCopiesAvailable = 0;
     foreach my $itemInfo (@{$biblioData->{itemInfos}}) {
@@ -369,7 +408,8 @@ foreach my $biblioNum (@biblionumbers) {
         $itemLoopIter->{barcode} = $itemInfo->{barcode};
         $itemLoopIter->{homeBranchName} = $branches->{$itemInfo->{homebranch}}{branchname};
         $itemLoopIter->{callNumber} = $itemInfo->{itemcallnumber};
-
+        $itemLoopIter->{enumchron} = $itemInfo->{enumchron};
+        $itemLoopIter->{copynumber} = $itemInfo->{copynumber};
         if ($itemLevelTypes) {
             $itemLoopIter->{description} = $itemInfo->{description};
             $itemLoopIter->{imageurl} = $itemInfo->{imageurl};
@@ -386,7 +426,7 @@ foreach my $biblioNum (@biblionumbers) {
         # change the background color.
         my $issues= GetItemIssue($itemNum);
         if ( $issues->{'date_due'} ) {
-            $itemLoopIter->{dateDue} = format_date($issues->{'date_due'});
+            $itemLoopIter->{dateDue} = format_sqlduedatetime($issues->{date_due});
             $itemLoopIter->{backgroundcolor} = 'onloan';
         }
 
@@ -394,13 +434,18 @@ foreach my $biblioNum (@biblionumbers) {
         my ($reservedate,$reservedfor,$expectedAt) = GetReservesFromItemnumber($itemNum);
         my $ItemBorrowerReserveInfo = GetMemberDetails( $reservedfor, 0);
 
+       # the item could be reserved for this borrower vi a host record, flag this
+       if ($reservedfor eq $borrowernumber){
+               $itemLoopIter->{already_reserved} = 1;
+       }
+
         if ( defined $reservedate ) {
             $itemLoopIter->{backgroundcolor} = 'reserved';
             $itemLoopIter->{reservedate}     = format_date($reservedate);
-            $itemLoopIter->{ReservedForBorrowernumber}     = $reservedfor;
-            $itemLoopIter->{ReservedForSurname}     = $ItemBorrowerReserveInfo->{'surname'};
-            $itemLoopIter->{ReservedForFirstname}     = $ItemBorrowerReserveInfo->{'firstname'};
-            $itemLoopIter->{ExpectedAtLibrary}     = $expectedAt;
+            $itemLoopIter->{ReservedForBorrowernumber} = $reservedfor;
+            $itemLoopIter->{ReservedForSurname}        = $ItemBorrowerReserveInfo->{'surname'};
+            $itemLoopIter->{ReservedForFirstname}      = $ItemBorrowerReserveInfo->{'firstname'};
+            $itemLoopIter->{ExpectedAtLibrary}         = $expectedAt;
         }
 
         $itemLoopIter->{notforloan} = $itemInfo->{notforloan};
@@ -435,10 +480,19 @@ foreach my $biblioNum (@biblionumbers) {
             $itemLoopIter->{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} = GetBiblioData($itemInfo->{biblionumber})->{title};
+       }
+
         # If there is no loan, return and transfer, we show a checkbox.
         $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
 
-        my $branchitemrule = GetBranchItemRule( $borr->{'branchcode'}, $itemInfo->{'itype'} );
+        my $branch = GetReservesControlBranch( $itemInfo, $borr );
+
+        my $branchitemrule = GetBranchItemRule( $branch, $itemInfo->{'itype'} );
         my $policy_holdallowed = 1;
 
         if ( $branchitemrule->{'holdallowed'} == 0 ||
@@ -446,7 +500,7 @@ foreach my $biblioNum (@biblionumbers) {
             $policy_holdallowed = 0;
         }
 
-        if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed) {
+        if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) and ($itemLoopIter->{already_reserved} ne 1)) {
             $itemLoopIter->{available} = 1;
             $numCopiesAvailable++;
         }
@@ -460,9 +514,14 @@ foreach my $biblioNum (@biblionumbers) {
         }
        $itemLoopIter->{imageurl} = getitemtypeimagelocation( 'opac', $itemTypes->{ $itemInfo->{itype} }{imageurl} );
 
-        
+    # Show serial enumeration when needed
+        if ($itemLoopIter->{enumchron}) {
+            $itemdata_enumchron = 1;
+        }
+
         push @{$biblioLoopIter{itemLoop}}, $itemLoopIter;
     }
+    $template->param( itemdata_enumchron => $itemdata_enumchron );
 
     if ($numCopiesAvailable > 0) {
         $numBibsAvailable++;
@@ -472,35 +531,49 @@ foreach my $biblioNum (@biblionumbers) {
     if ($biblioLoopIter{already_reserved}) {
         $biblioLoopIter{holdable} = undef;
     }
+    if(not CanBookBeReserved($borrowernumber,$biblioNum)){
+        $biblioLoopIter{holdable} = undef;
+    }
+    if(not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowernumber,$biblioNum)) {
+        $biblioLoopIter{holdable} = undef;
+        $biblioLoopIter{already_patron_possession} = 1;
+    }
+
+    if( $biblioLoopIter{holdable} ){ $anyholdable++; }
 
     push @$biblioLoop, \%biblioLoopIter;
 }
 
-
-if ( $numBibsAvailable == 0 ) {
-    $template->param( none_available => 1, message => 1 );
+if ( $numBibsAvailable == 0 || $anyholdable == 0) {
+    $template->param( none_available => 1 );
 }
 
-
-my $itemTableColspan = 5;
-if (!$template->param('OPACItemHolds')) {
+my $itemTableColspan = 9;
+if (! $template->{VARS}->{'OPACItemHolds'}) {
     $itemTableColspan--;
 }
-if ($template->param('singleBranchMode')) {
+if (! $template->{VARS}->{'singleBranchMode'}) {
     $itemTableColspan--;
 }
+$itemTableColspan-- if !$show_holds_count && !$show_priority;
+my $show_notes=C4::Context->preference('OpacHoldNotes');
+$template->param(OpacHoldNotes=>$show_notes);
+$itemTableColspan-- if !$show_notes;
 $template->param(itemtable_colspan => $itemTableColspan);
 
-
-
 # display infos
 $template->param(bibitemloop => $biblioLoop);
-output_html_with_http_headers $query, $cookie, $template->output;
-
-
-
+$template->param( showholds=>$show_holds_count);
+$template->param( showpriority=>$show_priority);
+# can set reserve date in future
+if (
+    C4::Context->preference( 'AllowHoldDateInFuture' ) &&
+    C4::Context->preference( 'OPACAllowHoldDateInFuture' )
+    ) {
+    $template->param(
+           reserve_in_future         => 1,
+    );
+}
 
+output_html_with_http_headers $query, $cookie, $template->output;
 
-# Local Variables:
-# tab-width: 8
-# End: