Bug 22789: Add non priority feature to C4 classes and staff interface
[srvgit] / reserve / request.pl
index f559ed0..b1e8745 100755 (executable)
@@ -48,11 +48,12 @@ use Koha::Biblios;
 use Koha::DateUtils;
 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;
 
 my $dbh = C4::Context->dbh;
 my $input = new CGI;
@@ -75,8 +76,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;
@@ -101,8 +106,9 @@ if ( $action eq 'move' ) {
   }
 } 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 if $hold;
+  $hold->cancel({ cancellation_reason => $cancellation_reason }) if $hold;
 } elsif ( $action eq 'setLowestPriority' ) {
   my $reserve_id = $input->param('reserve_id');
   ToggleLowestPriority( $reserve_id );
@@ -135,6 +141,25 @@ 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.'%' } }
+        ] );
+        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 +169,8 @@ 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 have the borrowernumber because we've performed an action, then we
 # don't want to try to place another reserve.
@@ -203,7 +227,54 @@ if ($borrowernumber_hold && !$action) {
     );
 }
 
-$template->param( messageborrower => $messageborrower );
+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->unblessed };
+        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;
+        }
+        my $expiry_date = $enrollment->patron->dateexpiry;
+        $member->{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)) {
+            $member->{expiry} = 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
+    );
+}
+
+$template->param(
+    messageborrower => $messageborrower,
+    messageclub     => $messageclub
+);
 
 # FIXME launch another time GetMember perhaps until (Joubu: Why?)
 my $patron = Koha::Patrons->find( $borrowernumber_hold );
@@ -227,7 +298,7 @@ foreach my $biblionumber (@biblionumbers) {
     my $force_hold_level;
     if ( $patron ) {
         { # CanBookBeReserved
-            my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber, $pickup );
+            my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber );
             if ( $canReserve->{status} eq 'OK' ) {
 
                 #All is OK and we can continue
@@ -346,10 +417,6 @@ foreach my $biblionumber (@biblionumbers) {
           }
     };
 
-    my $frameworkcode = GetFrameworkCode( $biblionumber );
-    my @notforloan_avs = Koha::AuthorisedValues->search_by_koha_field({ kohafield => 'items.notforloan', frameworkcode => $frameworkcode });
-    my $notforloan_label_of = { map { $_->authorised_value => $_->lib } @notforloan_avs };
-
     my @bibitemloop;
 
     my @available_itemtypes;
@@ -358,6 +425,7 @@ foreach my $biblionumber (@biblionumbers) {
         my $num_available = 0;
         my $num_override  = 0;
         my $hiddencount   = 0;
+        my $num_alreadyheld = 0;
 
         $biblioitem->{force_hold_level} = $force_hold_level;
 
@@ -377,6 +445,15 @@ foreach my $biblionumber (@biblionumbers) {
                 $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 => $biblioitemnumber, patron => $patron })
+            if $patron;
+
         foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
             my $item = $iteminfos_of->{$itemnumber};
             my $do_check;
@@ -384,7 +461,7 @@ foreach my $biblionumber (@biblionumbers) {
                 $do_check = $patron->do_check_for_previous_checkout($item) if $wants_check;
                 if ( $do_check && $wants_check ) {
                     $item->{checked_previously} = $do_check;
-                    if ( $multihold ) {
+                    if ( $multi_hold ) {
                         $biblioloopiter{checked_previously} = $do_check;
                     } else {
                         $template->param( checked_previously => $do_check );
@@ -436,18 +513,10 @@ foreach my $biblionumber (@biblionumbers) {
             # Management of the notforloan document
             if ( $item->{notforloan} ) {
                 $item->{backgroundcolor} = 'other';
-                $item->{notforloanvalue} =
-                  $notforloan_label_of->{ $item->{notforloan} };
             }
 
             # Management of lost or long overdue items
             if ( $item->{itemlost} ) {
-
-                # FIXME localized strings should never be in Perl code
-                $item->{message} =
-                  $item->{itemlost} == 1 ? "(lost)"
-                    : $item->{itemlost} == 2 ? "(long overdue)"
-                      : "";
                 $item->{backgroundcolor} = 'other';
                 if ($logged_in_patron->category->hidelostitems && !$showallitems) {
                     $item->{hide} = 1;
@@ -489,27 +558,44 @@ foreach my $biblionumber (@biblionumbers) {
 
                 $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
 
-                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' );
+                my $can_item_be_reserved = CanItemBeReserved( $patron->borrowernumber, $itemnumber )->{status};
+                $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
 
-                $item->{item_level_holds} = Koha::IssuingRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
+                $item->{item_level_holds} = Koha::CirculationRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
 
                 if (
                        !$item->{cantreserve}
                     && !$exceeded_maxreserves
-                    && IsAvailableForItemLevelRequest($item_object, $patron)
-                    && $can_item_be_reserved->{status} eq 'OK'
+                    && $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)
                   )
                 {
                     $item->{available} = 1;
                     $num_available++;
+                    if($branchitemrule->{'hold_fulfillment_policy'} eq 'any' ) {
+                        $item->{pickup_locations} = 'Any library';
+                        $item->{pickup_locations_code} = 'all';
+                    } else {
+                        my $arr_locations = Koha::Items->find($itemnumber)
+                                    ->pickup_locations( { patron => $patron } );
+
+                        $item->{pickup_locations} = join( ', ',
+                            map { $_->unblessed->{branchname} } @$arr_locations);
+                        $item->{pickup_locations_code} = join( ',',
+                            map { $_->unblessed->{branchcode} } @$arr_locations);
+                    }
 
                     push( @available_itemtypes, $item->{itype} );
                 }
                 elsif ( C4::Context->preference('AllowHoldPolicyOverride') ) {
                     # If AllowHoldPolicyOverride is set, it should override EVERY restriction, not just branch item rules
-                    $item->{override} = 1;
-                    $num_override++;
+                    # 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++;
+                    } else { $num_alreadyheld++ }
 
                     push( @available_itemtypes, $item->{itype} );
                 }
@@ -529,7 +615,10 @@ foreach my $biblionumber (@biblionumbers) {
             push @{ $biblioitem->{itemloop} }, $item;
         }
 
-        if ( $num_override == scalar( @{ $biblioitem->{itemloop} } ) ) { # That is, if all items require an override
+        # 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 );
@@ -599,9 +688,8 @@ foreach my $biblionumber (@biblionumbers) {
             }
         }
 
-        $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{'expirationdate'} = $res->expirationdate;
+        $reserve{'date'}           = $res->reservedate;
         $reserve{'borrowernumber'} = $res->borrowernumber();
         $reserve{'biblionumber'}   = $res->biblionumber();
         $reserve{'patron'}         = $res->borrower;
@@ -639,9 +727,8 @@ foreach my $biblionumber (@biblionumbers) {
                      date              => $date,
                      biblionumber      => $biblionumber,
                      findborrower      => $findborrower,
-                     title             => $biblio->title,
-                     author            => $biblio->author,
-                     holdsview => 1,
+                     biblio            => $biblio,
+                     holdsview         => 1,
                      C4::Search::enabled_staff_search_views,
                     );