Bug 27673: Fix t/Context.t
[koha-ffzg.git] / C4 / Reserves.pm
index 322002d..a658646 100644 (file)
@@ -69,10 +69,13 @@ This modules provides somes functions to deal with reservations.
   The following columns contains important values :
   - priority >0      : then the reserve is at 1st stage, and not yet affected to any item.
              =0      : then the reserve is being dealed
-  - found : NULL       : means the patron requested the 1st available, and we haven't chosen the item
-            T(ransit)  : the reserve is linked to an item but is in transit to the pickup branch
-            W(aiting)  : the reserve is linked to an item, is at the pickup branch, and is waiting on the hold shelf
-            F(inished) : the reserve has been completed, and is done
+  - found : NULL         : means the patron requested the 1st available, and we haven't chosen the item
+            T(ransit)    : the reserve is linked to an item but is in transit to the pickup branch
+            W(aiting)    : the reserve is linked to an item, is at the pickup branch, and is waiting on the hold shelf
+            F(inished)   : the reserve has been completed, and is done
+            P(rocessing) : reserved item has been returned using self-check machine and reserve needs to be confirmed
+                           by librarian before notice is send and status changed to waiting.
+                           Applicable only if HoldsNeedProcessingSIP system preference is set.
   - itemnumber : empty : the reserve is still unaffected to an item
                  filled: the reserve is attached to an item
   The complete workflow is :
@@ -318,6 +321,12 @@ See CanItemBeReserved() for possible return values.
 sub CanBookBeReserved{
     my ($borrowernumber, $biblionumber, $pickup_branchcode, $params) = @_;
 
+    # Check that patron have not checked out this biblio (if AllowHoldsOnPatronsPossessions set)
+    if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
+        && C4::Circulation::CheckIfIssuedToPatron( $borrowernumber, $biblionumber ) ) {
+        return { status =>'alreadypossession' };
+    }
+
     my @itemnumbers = Koha::Items->search({ biblionumber => $biblionumber})->get_column("itemnumber");
     #get items linked via host records
     my @hostitems = get_hostitemnumbers_of($biblionumber);
@@ -361,9 +370,7 @@ sub CanItemBeReserved {
 
     my $dbh = C4::Context->dbh;
     my $ruleitemtype;    # itemtype of the matching issuing rule
-    my $allowedreserves  = 0; # Total number of holds allowed across all records
-    my $holds_per_record = 1; # Total number of holds allowed for this one given record
-    my $holds_per_day;        # Default to unlimited
+    my $allowedreserves  = 0; # Total number of holds allowed across all records, default to none
 
     # we retrieve borrowers and items informations #
     # item->{itype} will come for biblioitems if necessery
@@ -386,6 +393,12 @@ sub CanItemBeReserved {
     return { status =>'itemAlreadyOnHold' }
       if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
 
+    # Check that patron have not checked out this biblio (if AllowHoldsOnPatronsPossessions set)
+    if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
+        && C4::Circulation::CheckIfIssuedToPatron( $patron->borrowernumber, $biblio->biblionumber ) ) {
+        return { status =>'alreadypossession' };
+    }
+
     my $controlbranch = C4::Context->preference('ReservesControlBranch');
 
     my $querycount = q{
@@ -410,16 +423,30 @@ sub CanItemBeReserved {
     }
 
     # we retrieve rights
-    if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->effective_itemtype, $branchcode ) ) {
-        $ruleitemtype     = $rights->{itemtype};
-        $allowedreserves  = $rights->{reservesallowed} // $allowedreserves;
-        $holds_per_record = $rights->{holds_per_record} // $holds_per_record;
-        $holds_per_day    = $rights->{holds_per_day};
+    if (
+        my $reservesallowed = Koha::CirculationRules->get_effective_rule({
+                itemtype     => $item->effective_itemtype,
+                categorycode => $borrower->{categorycode},
+                branchcode   => $branchcode,
+                rule_name    => 'reservesallowed',
+        })
+    ) {
+        $ruleitemtype     = $reservesallowed->itemtype;
+        $allowedreserves  = $reservesallowed->rule_value // 0; #undefined is 0, blank is unlimited
     }
     else {
         $ruleitemtype = undef;
     }
 
+    my $rights = Koha::CirculationRules->get_effective_rules({
+        categorycode => $borrower->{'categorycode'},
+        itemtype     => $item->effective_itemtype,
+        branchcode   => $branchcode,
+        rules        => ['holds_per_record','holds_per_day']
+    });
+    my $holds_per_record = $rights->{holds_per_record} // 1;
+    my $holds_per_day    = $rights->{holds_per_day};
+
     my $search_params = {
         borrowernumber => $borrowernumber,
         biblionumber   => $item->biblionumber,
@@ -714,6 +741,7 @@ sub GetReserveStatus {
 
     if(defined $found) {
         return 'Waiting'  if $found eq 'W' and $priority == 0;
+        return 'Processing'  if $found eq 'P';
         return 'Finished' if $found eq 'F';
     }
 
@@ -828,8 +856,10 @@ sub CheckReserves {
             if ( $res->{'itemnumber'} && $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) {
                 if ($res->{'found'} eq 'W') {
                     return ( "Waiting", $res, \@reserves ); # Found it, it is waiting
-                } else {
-                    return ( "Reserved", $res, \@reserves ); # Found determinated hold, e. g. the tranferred one
+                } elsif ($res->{'found'} eq 'P') {
+                    return ( "Processing", $res, \@reserves ); # Found determinated hold, e. g. the transferred one
+                 } else {
+                    return ( "Reserved", $res, \@reserves ); # Found determinated hold, e. g. the transferred one
                 }
             } else {
                 my $patron;
@@ -898,6 +928,7 @@ Cancels all reserves with an expiration date from before today.
 =cut
 
 sub CancelExpiredReserves {
+    my $cancellation_reason = shift;
     my $today = dt_from_string();
     my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays');
     my $expireWaiting = C4::Context->preference('ExpireReservesMaxPickUpDelay');
@@ -915,7 +946,8 @@ sub CancelExpiredReserves {
         next if !$cancel_on_holidays && $calendar->is_holiday( $today );
 
         my $cancel_params = {};
-        if ( $hold->found eq 'W' ) {
+        $cancel_params->{cancellation_reason} = $cancellation_reason if defined($cancellation_reason);
+        if ( defined($hold->found) && $hold->found eq 'W' ) {
             $cancel_params->{charge_cancel_fee} = 1;
         }
         $hold->cancel( $cancel_params );
@@ -1119,7 +1151,7 @@ sub ModReserveStatus {
 
 =head2 ModReserveAffect
 
-  &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend,$reserve_id);
+  &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend,$reserve_id, $desk_id);
 
 This function affect an item and a status for a given reserve, either fetched directly
 by record_id, or by borrowernumber and itemnumber or biblionumber. If only biblionumber
@@ -1133,7 +1165,7 @@ take care of the waiting status
 =cut
 
 sub ModReserveAffect {
-    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id ) = @_;
+    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id, $desk_id ) = @_;
     my $dbh = C4::Context->dbh;
 
     # we want to attach $itemnumber to $borrowernumber, find the biblionumber
@@ -1159,8 +1191,14 @@ sub ModReserveAffect {
 
     $hold->itemnumber($itemnumber);
 
-    if( !$transferToDo ){
-        $hold->set_waiting();
+    if ($transferToDo) {
+        $hold->set_transfer();
+    } elsif (C4::Context->preference('HoldsNeedProcessingSIP')
+             && C4::Context->interface eq 'sip'
+             && !$already_on_shelf) {
+        $hold->set_processing();
+    } else {
+        $hold->set_waiting($desk_id);
         _koha_notify_reserve( $hold->reserve_id ) unless $already_on_shelf;
         my $transfers = Koha::Item::Transfers->search({
             itemnumber => $itemnumber,
@@ -1169,9 +1207,7 @@ sub ModReserveAffect {
         while( my $transfer = $transfers->next ){
             $transfer->datearrived( dt_from_string() )->store;
         };
-     } else {
-        $hold->set_transfer();
-     }
+    }
 
 
     _FixPriority( { biblionumber => $biblionumber } );
@@ -1296,7 +1332,7 @@ sub IsAvailableForItemLevelRequest {
             GetReservesControlBranch( $item->unblessed(), $patron->unblessed() );
         my $branchitemrule =
             C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->itype );
-        my $home_library = Koka::Libraries->find( {branchcode => $item->homebranch} );
+        my $home_library = Koha::Libraries->find( {branchcode => $item->homebranch} );
         return 0 unless $branchitemrule->{hold_fulfillment_policy} ne 'holdgroup' || $home_library->validate_hold_sibling( {branchcode => $pickup_branchcode} );
     }
 
@@ -1550,7 +1586,7 @@ sub _FixPriority {
             UPDATE reserves
             SET    priority = 0
             WHERE reserve_id = ?
-            AND found IN ('W', 'T')
+            AND found IN ('W', 'T', 'P')
         ";
         my $sth = $dbh->prepare($query);
         $sth->execute( $reserve_id );
@@ -1562,7 +1598,7 @@ sub _FixPriority {
         SELECT reserve_id, borrowernumber, reservedate
         FROM   reserves
         WHERE  biblionumber   = ?
-          AND  ((found <> 'W' AND found <> 'T') OR found IS NULL)
+          AND  ((found <> 'W' AND found <> 'T' AND found <> 'P') OR found IS NULL)
         ORDER BY priority ASC
     ";
     my $sth = $dbh->prepare($query);
@@ -1834,7 +1870,8 @@ sub _koha_notify_reserve {
         next if (
                ( $mtt eq 'email' and not $to_address ) # No email address
             or ( $mtt eq 'sms'   and not $patron->smsalertnumber ) # No SMS number
-            or ( $mtt eq 'phone' and C4::Context->preference('TalkingTechItivaPhoneNotification') ) # Notice is handled by TalkingTech_itiva_outbound.pl
+            or ( $mtt eq 'itiva' and C4::Context->preference('TalkingTechItivaPhoneNotification') ) # Notice is handled by TalkingTech_itiva_outbound.pl
+            or ( $mtt eq 'phone' and not $patron->phone ) # No phone number to call
         );
 
         &$send_notification($mtt, $letter_code);
@@ -1971,13 +2008,13 @@ sub MergeHolds {
         # don't reorder those already waiting
 
         $sth = $dbh->prepare(
-"SELECT * FROM reserves WHERE biblionumber = ? AND (found <> ? AND found <> ? OR found is NULL) ORDER BY reservedate ASC"
+"SELECT * FROM reserves WHERE biblionumber = ? AND (found NOT IN ('W', 'T', 'P') OR found is NULL) ORDER BY reservedate ASC"
         );
         my $upd_sth = $dbh->prepare(
 "UPDATE reserves SET priority = ? WHERE biblionumber = ? AND borrowernumber = ?
         AND reservedate = ? AND (itemnumber = ? or itemnumber is NULL) "
         );
-        $sth->execute( $to_biblio, 'W', 'T' );
+        $sth->execute( $to_biblio );
         my $priority = 1;
         while ( my $reserve = $sth->fetchrow_hashref() ) {
             $upd_sth->execute(
@@ -2147,7 +2184,7 @@ sub CalculatePriority {
         AND   priority > 0
         AND   (found IS NULL OR found = '')
     };
-    #skip found==W or found==T (waiting or transit holds)
+    #skip found==W or found==T or found==P (waiting, transit or processing holds)
     if( $resdate ) {
         $sql.= ' AND ( reservedate <= ? )';
     }
@@ -2218,61 +2255,17 @@ sub GetMaxPatronHoldsForRecord {
 
         $branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
 
-        my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
-        my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
-        $max = $holds_per_record if $holds_per_record > $max;
-    }
-
-    return $max;
-}
-
-=head2 GetHoldRule
-
-my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
-
-Returns the matching hold related issuingrule fields for a given
-patron category, itemtype, and library.
-
-=cut
-
-sub GetHoldRule {
-    my ( $categorycode, $itemtype, $branchcode ) = @_;
-
-    my $reservesallowed = Koha::CirculationRules->get_effective_rule(
-        {
-            itemtype     => $itemtype,
+        my $rule = Koha::CirculationRules->get_effective_rule({
             categorycode => $categorycode,
-            branchcode   => $branchcode,
-            rule_name    => 'reservesallowed',
-            order_by     => {
-                -desc => [ 'categorycode', 'itemtype', 'branchcode' ]
-            }
-        }
-    );
-
-    my $rules;
-    if ( $reservesallowed ) {
-        $rules->{reservesallowed} = $reservesallowed->rule_value;
-        $rules->{itemtype}        = $reservesallowed->itemtype;
-        $rules->{categorycode}    = $reservesallowed->categorycode;
-        $rules->{branchcode}      = $reservesallowed->branchcode;
-    }
-
-    my $holds_per_x_rules = Koha::CirculationRules->get_effective_rules(
-        {
             itemtype     => $itemtype,
-            categorycode => $categorycode,
             branchcode   => $branchcode,
-            rules        => ['holds_per_record', 'holds_per_day'],
-            order_by     => {
-                -desc => [ 'categorycode', 'itemtype', 'branchcode' ]
-            }
-        }
-    );
-    $rules->{holds_per_record} = $holds_per_x_rules->{holds_per_record};
-    $rules->{holds_per_day} = $holds_per_x_rules->{holds_per_day};
+            rule_name    => 'holds_per_record'
+        });
+        my $holds_per_record = $rule ? $rule->rule_value : 0;
+        $max = $holds_per_record if $holds_per_record > $max;
+    }
 
-    return $rules;
+    return $max;
 }
 
 =head1 AUTHOR