Bug 15524: (QA follow-up) Respect ReservesControlBranch
[koha.git] / C4 / Reserves.pm
index 079fb39..16ad20d 100644 (file)
@@ -104,7 +104,6 @@ BEGIN {
     @EXPORT = qw(
         &AddReserve
 
-        &GetReservesForBranch
         &GetReserveStatus
 
         &GetOtherReserves
@@ -127,8 +126,6 @@ BEGIN {
 
         &IsAvailableForItemLevelRequest
 
-        &OPACItemHoldsAllowed
-
         &AlterPriority
         &ToggleLowestPriority
 
@@ -174,6 +171,16 @@ sub AddReserve {
 
     $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
 
+    # if we have an item selectionned, and the pickup branch is the same as the holdingbranch
+    # of the document, we force the value $priority and $found .
+    if ( $checkitem and not C4::Context->preference('ReservesNeedReturns') ) {
+        $priority = 0;
+        my $item = Koha::Items->find( $checkitem ); # FIXME Prevent bad calls
+        if ( $item->holdingbranch eq $branch ) {
+            $found = 'W';
+        }
+    }
+
     if ( C4::Context->preference('AllowHoldDateInFuture') ) {
 
         # Make room in reserves for this before those of a later reserve date
@@ -206,6 +213,7 @@ sub AddReserve {
             itemtype       => $itemtype,
         }
     )->store();
+    $hold->set_waiting() if $found eq 'W';
 
     logaction( 'HOLDS', 'CREATE', $hold->id, Dumper($hold->unblessed) )
         if C4::Context->preference('HoldsLog');
@@ -401,6 +409,29 @@ sub CanItemBeReserved {
         return 'tooManyReserves';
     }
 
+    # Now we need to check hold limits by patron category
+    my $schema = Koha::Database->new()->schema();
+    my $rule = $schema->resultset('BranchBorrowerCircRule')->find(
+        {
+            branchcode   => $branchcode,
+            categorycode => $borrower->{categorycode},
+        }
+    );
+    $rule ||= $schema->resultset('DefaultBorrowerCircRule')->find(
+        {
+            categorycode => $borrower->{categorycode}
+        }
+    );
+    if ( $rule && defined $rule->max_holds ) {
+        my $total_holds_count = Koha::Holds->search(
+            {
+                borrowernumber => $borrower->{borrowernumber}
+            }
+        )->count();
+
+        return 'tooManyReserves' if $total_holds_count >= $rule->max_holds;
+    }
+
     my $circ_control_branch =
       C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
     my $branchitemrule =
@@ -562,41 +593,6 @@ SELECT COUNT(*) FROM reserves WHERE biblionumber=? AND borrowernumber<>?
     return $fee;
 }
 
-=head2 GetReservesForBranch
-
-  @transreserv = GetReservesForBranch($frombranch);
-
-=cut
-
-sub GetReservesForBranch {
-    my ($frombranch) = @_;
-    my $dbh = C4::Context->dbh;
-
-    my $query = "
-        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate, expirationdate
-        FROM   reserves
-        WHERE   priority='0'
-        AND found='W'
-    ";
-    $query .= " AND branchcode=? " if ( $frombranch );
-    $query .= "ORDER BY waitingdate" ;
-
-    my $sth = $dbh->prepare($query);
-    if ($frombranch){
-     $sth->execute($frombranch);
-    } else {
-        $sth->execute();
-    }
-
-    my @transreserv;
-    my $i = 0;
-    while ( my $data = $sth->fetchrow_hashref ) {
-        $transreserv[$i] = $data;
-        $i++;
-    }
-    return (@transreserv);
-}
-
 =head2 GetReserveStatus
 
   $reservestatus = GetReserveStatus($itemnumber);
@@ -732,7 +728,11 @@ sub CheckReserves {
         my $priority = 10000000;
         foreach my $res (@reserves) {
             if ( $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) {
-                return ( "Waiting", $res, \@reserves ); # Found it
+                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
+                }
             } else {
                 my $patron;
                 my $iteminfo;
@@ -792,20 +792,16 @@ Cancels all reserves with an expiration date from before today.
 =cut
 
 sub CancelExpiredReserves {
-
     my $today = dt_from_string();
     my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays');
-
-    my $dbh = C4::Context->dbh;
+    my $expireWaiting = C4::Context->preference('ExpireReservesMaxPickUpDelay');
 
     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
-    my $today = dt_from_string;
+    my $params = { expirationdate => { '<', $dtf->format_date($today) } };
+    $params->{found} = undef unless $expireWaiting;
+
     # FIXME To move to Koha::Holds->search_expired (?)
-    my $holds = Koha::Holds->search(
-        {
-            expirationdate => { '<', $dtf->format_date($today) }
-        }
-    );
+    my $holds = Koha::Holds->search( $params );
 
     while ( my $hold = $holds->next ) {
         my $calendar = Koha::Calendar->new( branchcode => $hold->branchcode );
@@ -813,11 +809,10 @@ sub CancelExpiredReserves {
         next if !$cancel_on_holidays && $calendar->is_holiday( $today );
 
         my $cancel_params = {};
-        if ( $holds->found eq 'W' ) {
+        if ( $hold->found eq 'W' ) {
             $cancel_params->{charge_cancel_fee} = 1;
         }
         $hold->cancel( $cancel_params );
-
     }
 }
 
@@ -832,7 +827,7 @@ Unsuspends all suspended reserves with a suspend_until date from before today.
 sub AutoUnsuspendReserves {
     my $today = dt_from_string();
 
-    my @holds = Koha::Holds->search( { suspend_until => { '<' => $today->ymd() } } );
+    my @holds = Koha::Holds->search( { suspend_until => { '<=' => $today->ymd() } } );
 
     map { $_->suspend(0)->suspend_until(undef)->store() } @holds;
 }
@@ -891,8 +886,9 @@ sub ModReserve {
 
     my $hold;
     unless ( $reserve_id ) {
-        $hold = Koha::Holds->search({ biblionumber => $biblionumber, borrowernumber => $borrowernumber, itemnumber => $itemnumber });
-        return unless $hold; # FIXME Should raise an exception
+        my $holds = Koha::Holds->search({ biblionumber => $biblionumber, borrowernumber => $borrowernumber, itemnumber => $itemnumber });
+        return unless $holds->count; # FIXME Should raise an exception
+        $hold = $holds->next;
         $reserve_id = $hold->reserve_id;
     }
 
@@ -1116,6 +1112,7 @@ item-level hold request.  An item is available if
 * it is not lost AND
 * it is not damaged AND
 * it is not withdrawn AND
+* a waiting or in transit reserve is placed on
 * does not have a not for loan value > 0
 
 Need to check the issuingrules onshelfholds column,
@@ -1138,10 +1135,12 @@ sub IsAvailableForItemLevelRequest {
     # FIXME - a lot of places in the code do this
     #         or something similar - need to be
     #         consolidated
-    my $itype = _get_itype($item);
+    my $patron = Koha::Patrons->find( $borrower->{borrowernumber} );
+    my $item_object = Koha::Items->find( $item->{itemnumber } );
+    my $itemtype = $item_object->effective_itemtype;
     my $notforloan_per_itemtype
       = $dbh->selectrow_array("SELECT notforloan FROM itemtypes WHERE itemtype = ?",
-                              undef, $itype);
+                              undef, $itemtype);
 
     return 0 if
         $notforloan_per_itemtype ||
@@ -1150,7 +1149,7 @@ sub IsAvailableForItemLevelRequest {
         $item->{withdrawn}        ||
         ($item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems'));
 
-    my $on_shelf_holds = _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
+    my $on_shelf_holds = Koha::IssuingRules->get_onshelfholds_policy( { item => $item_object, patron => $patron } );
 
     if ( $on_shelf_holds == 1 ) {
         return 1;
@@ -1161,6 +1160,10 @@ sub IsAvailableForItemLevelRequest {
         my $any_available = 0;
 
         foreach my $i (@items) {
+
+            my $circ_control_branch = C4::Circulation::_GetCircControlBranch( $i->unblessed(), $borrower );
+            my $branchitemrule = C4::Circulation::GetBranchItemRule( $circ_control_branch, $i->itype );
+
             $any_available = 1
               unless $i->itemlost
               || $i->notforloan > 0
@@ -1169,29 +1172,14 @@ sub IsAvailableForItemLevelRequest {
               || IsItemOnHoldAndFound( $i->id )
               || ( $i->damaged
                 && !C4::Context->preference('AllowHoldsOnDamagedItems') )
-              || Koha::ItemTypes->find( $i->effective_itemtype() )->notforloan;
+              || Koha::ItemTypes->find( $i->effective_itemtype() )->notforloan
+              || $branchitemrule->{holdallowed} == 1 && $borrower->{branchcode} ne $i->homebranch;
         }
 
         return $any_available ? 0 : 1;
+    } else { # on_shelf_holds == 0 "If any unavailable" (the description is rather cryptic and could still be improved)
+        return $item->{onloan} || IsItemOnHoldAndFound( $item->{itemnumber} );
     }
-
-    return $item->{onloan} || GetReserveStatus($item->{itemnumber}) eq "Waiting";
-}
-
-=head2 OnShelfHoldsAllowed
-
-  OnShelfHoldsAllowed($itemtype,$borrowercategory,$branchcode);
-
-Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see if onshelf
-holds are allowed, returns true if so.
-
-=cut
-
-sub OnShelfHoldsAllowed {
-    my ($item, $borrower) = @_;
-
-    my $itype = _get_itype($item);
-    return _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
 }
 
 sub _get_itype {
@@ -1221,13 +1209,6 @@ sub _get_itype {
     return $itype;
 }
 
-sub _OnShelfHoldsAllowed {
-    my ($itype,$borrowercategory,$branchcode) = @_;
-
-    my $issuing_rule = Koha::IssuingRules->get_effective_issuing_rule({ categorycode => $borrowercategory, itemtype => $itype, branchcode => $branchcode });
-    return $issuing_rule ? $issuing_rule->onshelfholds : undef;
-}
-
 =head2 AlterPriority
 
   AlterPriority( $where, $reserve_id );
@@ -1642,7 +1623,7 @@ sub _koha_notify_reserve {
     my $patron = Koha::Patrons->find( $borrowernumber );
 
     # Try to get the borrower's email address
-    my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
+    my $to_address = $patron->notice_email_address;
 
     my $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( {
             borrowernumber => $borrowernumber,
@@ -1753,54 +1734,6 @@ sub _ShiftPriorityByDateAndPriority {
     return $new_priority;  # so the caller knows what priority they wind up receiving
 }
 
-=head2 OPACItemHoldsAllowed
-
-  OPACItemHoldsAllowed($item_record,$borrower_record);
-
-Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see
-if specific item holds are allowed, returns true if so.
-
-=cut
-
-sub OPACItemHoldsAllowed {
-    my ($item,$borrower) = @_;
-
-    my $branchcode = $item->{homebranch} or die "No homebranch";
-    my $itype;
-    my $dbh = C4::Context->dbh;
-    if (C4::Context->preference('item-level_itypes')) {
-       # We can't trust GetItem to honour the syspref, so safest to do it ourselves
-       # When GetItem is fixed, we can remove this
-       $itype = $item->{itype};
-    }
-    else {
-       my $query = "SELECT itemtype FROM biblioitems WHERE biblioitemnumber = ? ";
-       my $sth = $dbh->prepare($query);
-       $sth->execute($item->{biblioitemnumber});
-       if (my $data = $sth->fetchrow_hashref()){
-           $itype = $data->{itemtype};
-       }
-    }
-
-    my $query = "SELECT opacitemholds,categorycode,itemtype,branchcode FROM issuingrules WHERE
-          (issuingrules.categorycode = ? OR issuingrules.categorycode = '*')
-        AND
-          (issuingrules.itemtype = ? OR issuingrules.itemtype = '*')
-        AND
-          (issuingrules.branchcode = ? OR issuingrules.branchcode = '*')
-        ORDER BY
-          issuingrules.categorycode desc,
-          issuingrules.itemtype desc,
-          issuingrules.branchcode desc
-       LIMIT 1";
-    my $sth = $dbh->prepare($query);
-    $sth->execute($borrower->{categorycode},$itype,$branchcode);
-    my $data = $sth->fetchrow_hashref;
-    my $opacitemholds = uc substr ($data->{opacitemholds}, 0, 1);
-    return '' if $opacitemholds eq 'N';
-    return $opacitemholds;
-}
-
 =head2 MoveReserve
 
   MoveReserve( $itemnumber, $borrowernumber, $cancelreserve )
@@ -1958,7 +1891,15 @@ sub RevertWaitingStatus {
 
 =head2 ReserveSlip
 
-  ReserveSlip($branchcode, $borrowernumber, $biblionumber)
+ReserveSlip(
+    {
+        branchcode     => $branchcode,
+        borrowernumber => $borrowernumber,
+        biblionumber   => $biblionumber,
+        [ itemnumber   => $itemnumber, ]
+        [ barcode      => $barcode, ]
+    }
+  )
 
 Returns letter hash ( see C4::Letters::GetPreparedLetter ) or undef
 
@@ -1975,19 +1916,44 @@ available within the slip:
 =cut
 
 sub ReserveSlip {
-    my ($branch, $borrowernumber, $biblionumber) = @_;
+    my ($args) = @_;
+    my $branchcode     = $args->{branchcode};
+    my $borrowernumber = $args->{borrowernumber};
+    my $biblionumber   = $args->{biblionumber};
+    my $itemnumber     = $args->{itemnumber};
+    my $barcode        = $args->{barcode};
 
-#   return unless ( C4::Context->boolean_preference('printreserveslips') );
-    my $patron = Koha::Patrons->find( $borrowernumber );
 
-    my $hold = Koha::Holds->search({biblionumber => $biblionumber, borrowernumber => $borrowernumber })->next;
+    my $patron = Koha::Patrons->find($borrowernumber);
+
+    my $hold;
+    if ($itemnumber || $barcode ) {
+        $itemnumber ||= Koha::Items->find( { barcode => $barcode } )->itemnumber;
+
+        $hold = Koha::Holds->search(
+            {
+                biblionumber   => $biblionumber,
+                borrowernumber => $borrowernumber,
+                itemnumber     => $itemnumber
+            }
+        )->next;
+    }
+    else {
+        $hold = Koha::Holds->search(
+            {
+                biblionumber   => $biblionumber,
+                borrowernumber => $borrowernumber
+            }
+        )->next;
+    }
+
     return unless $hold;
     my $reserve = $hold->unblessed;
 
     return  C4::Letters::GetPreparedLetter (
         module => 'circulation',
         letter_code => 'HOLD_SLIP',
-        branchcode => $branch,
+        branchcode => $branchcode,
         lang => $patron->lang,
         tables => {
             'reserves'    => $reserve,