Bug 24329: Add a test
[srvgit] / C4 / Reserves.pm
index b54b41d..503771e 100644 (file)
@@ -21,8 +21,7 @@ package C4::Reserves;
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 
-use strict;
-#use warnings; FIXME - Bug 2505
+use Modern::Perl;
 
 use C4::Accounts;
 use C4::Biblio;
@@ -173,9 +172,25 @@ sub AddReserve {
     # 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 ) {
+
+        if (
+            # If item is already checked out, it cannot be set waiting
+            !$item->onloan
+
+            # The item can't be waiting if it needs a transfer
+            && $item->holdingbranch eq $branch
+
+            # Similarly, if in transit it can't be waiting
+            && !$item->get_transfer
+
+            # If we can't hold damaged items, and it is damaged, it can't be waiting
+            && ( $item->damaged && C4::Context->preference('AllowHoldsOnDamagedItems') || !$item->damaged )
+
+            # Lastly, if this already has holds, we shouldn't make it waiting for the new hold
+            && !$item->current_holds->count )
+        {
+            $priority = 0;
             $found = 'W';
         }
     }
@@ -189,7 +204,7 @@ sub AddReserve {
     my $waitingdate;
 
     # If the reserv had the waiting status, we had the value of the resdate
-    if ( $found eq 'W' ) {
+    if ( $found && $found eq 'W' ) {
         $waitingdate = $resdate;
     }
 
@@ -213,7 +228,7 @@ sub AddReserve {
             item_level_hold => $checkitem ? 1 : 0,
         }
     )->store();
-    $hold->set_waiting() if $found eq 'W';
+    $hold->set_waiting() if $found && $found eq 'W';
 
     logaction( 'HOLDS', 'CREATE', $hold->id, Dumper($hold->unblessed) )
         if C4::Context->preference('HoldsLog');
@@ -299,11 +314,13 @@ sub CanBookBeReserved{
          { status => ageRestricted },   if the Item is age restricted for this borrower.
          { status => damaged },         if the Item is damaged.
          { status => cannotReserveFromOtherBranches }, if syspref 'canreservefromotherbranches' is OK.
+         { status => branchNotInHoldGroup }, if borrower home library is not in hold group, and holds are only allowed from hold groups.
          { status => tooManyReserves, limit => $limit }, if the borrower has exceeded their maximum reserve amount.
          { status => notReservable },   if holds on this item are not allowed
          { status => libraryNotFound },   if given branchcode is not an existing library
          { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
          { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
+         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
 
 =cut
 
@@ -458,6 +475,13 @@ sub CanItemBeReserved {
         return { status => 'cannotReserveFromOtherBranches' };
     }
 
+    my $item_library = Koha::Libraries->find( {branchcode => $item->homebranch} );
+    if ( $branchitemrule->{holdallowed} == 3) {
+        if($borrower->{branchcode} ne $item->homebranch && !$item_library->validate_hold_sibling( {branchcode => $borrower->{branchcode}} )) {
+            return { status => 'branchNotInHoldGroup' };
+        }
+    }
+
     # If reservecount is ok, we check item branch if IndependentBranches is ON
     # and canreservefromotherbranches is OFF
     if ( C4::Context->preference('IndependentBranches')
@@ -482,6 +506,12 @@ sub CanItemBeReserved {
         unless ($item->can_be_transferred({ to => $destination })) {
             return { status => 'cannotBeTransferred' };
         }
+        unless ($branchitemrule->{hold_fulfillment_policy} ne 'holdgroup' || $item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) {
+            return { status => 'pickupNotInHoldGroup' };
+        }
+        unless ($branchitemrule->{hold_fulfillment_policy} ne 'patrongroup' || Koha::Libraries->find({branchcode => $borrower->{branchcode}})->validate_hold_sibling({branchcode => $pickup_branchcode})) {
+            return { status => 'pickupNotInHoldGroup' };
+        }
     }
 
     return { status => 'OK' };
@@ -577,9 +607,8 @@ sub ChargeReserveFee {
             user_id      => C4::Context->userenv ? C4::Context->userenv->{'number'} : undef,
             library_id   => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef,
             interface    => C4::Context->interface,
-            sip          => undef,
             invoice_type => undef,
-            type         => 'reserve',
+            type         => 'RESERVE',
             item_id      => undef
         }
     );
@@ -664,9 +693,9 @@ sub GetReserveStatus {
 
 =head2 CheckReserves
 
-  ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber);
-  ($status, $reserve, $all_reserves) = &CheckReserves(undef, $barcode);
-  ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber,undef,$lookahead);
+  ($status, $matched_reserve, $possible_reserves) = &CheckReserves($itemnumber);
+  ($status, $matched_reserve, $possible_reserves) = &CheckReserves(undef, $barcode);
+  ($status, $matched_reserve, $possible_reserves) = &CheckReserves($itemnumber,undef,$lookahead);
 
 Find a book in the reserves.
 
@@ -796,8 +825,12 @@ sub CheckReserves {
                     my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$item->effective_itemtype);
                     next if ($branchitemrule->{'holdallowed'} == 0);
                     next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $patron->branchcode));
+                    my $library = Koha::Libraries->find({branchcode=>$item->homebranch});
+                    next if (($branchitemrule->{'holdallowed'} == 3) && (!$library->validate_hold_sibling({branchcode => $patron->branchcode}) ));
                     my $hold_fulfillment_policy = $branchitemrule->{hold_fulfillment_policy};
-                    next if ( ($branchitemrule->{hold_fulfillment_policy} ne 'any') && ($res->{branchcode} ne $item->$hold_fulfillment_policy) );
+                    next if ( ($hold_fulfillment_policy eq 'holdgroup') && (!$library->validate_hold_sibling({branchcode => $res->{branchcode}})) );
+                    next if ( ($hold_fulfillment_policy eq 'homebranch') && ($res->{branchcode} ne $item->$hold_fulfillment_policy) );
+                    next if ( ($hold_fulfillment_policy eq 'holdingbranch') && ($res->{branchcode} ne $item->$hold_fulfillment_policy) );
                     next unless $item->can_be_transferred( { to => scalar Koha::Libraries->find( $res->{branchcode} ) } );
                     $priority = $res->{'priority'};
                     $highest  = $res;
@@ -935,15 +968,21 @@ sub ModReserve {
         logaction( 'HOLDS', 'MODIFY', $hold->reserve_id, Dumper($hold->unblessed) )
             if C4::Context->preference('HoldsLog');
 
-        $hold->set(
-            {
-                priority    => $rank,
-                branchcode  => $branchcode,
-                itemnumber  => $itemnumber,
-                found       => undef,
-                waitingdate => undef
-            }
-        )->store();
+        my $properties = {
+            priority    => $rank,
+            branchcode  => $branchcode,
+            itemnumber  => $itemnumber,
+            found       => undef,
+            waitingdate => undef
+        };
+        if (exists $params->{reservedate}) {
+            $properties->{reservedate} = $params->{reservedate} || undef;
+        }
+        if (exists $params->{expirationdate}) {
+            $properties->{expirationdate} = $params->{expirationdate} || undef;
+        }
+
+        $hold->set($properties)->store();
 
         if ( defined( $suspend_until ) ) {
             if ( $suspend_until ) {
@@ -1028,7 +1067,9 @@ sub ModReserveStatus {
     $sth_set->execute( $newstatus, $itemnumber );
 
     my $item = Koha::Items->find($itemnumber);
-    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) && $newstatus ) {
+    if ( $item->location && $item->location eq 'CART'
+        && ( !$item->permanent_location || $item->permanent_location ne 'CART' )
+        && $newstatus ) {
       CartToShelf( $itemnumber );
     }
 }
@@ -1081,7 +1122,8 @@ sub ModReserveAffect {
 
     _FixPriority( { biblionumber => $biblionumber } );
     my $item = Koha::Items->find($itemnumber);
-    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) ) {
+    if ( $item->location && $item->location eq 'CART'
+        && ( !$item->permanent_location || $item->permanent_location ne 'CART' ) ) {
       CartToShelf( $itemnumber );
     }
 
@@ -1185,6 +1227,12 @@ sub IsAvailableForItemLevelRequest {
         return 0 unless $destination;
         return 0 unless $destination->pickup_location;
         return 0 unless $item->can_be_transferred( { to => $destination } );
+        my $reserves_control_branch =
+            GetReservesControlBranch( $item->unblessed(), $patron->unblessed() );
+        my $branchitemrule =
+            C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->itype );
+        my $home_library = Koka::Libraries->find( {branchcode => $item->homebranch} );
+        return 0 unless $branchitemrule->{hold_fulfillment_policy} ne 'holdgroup' || $home_library->validate_hold_sibling( {branchcode => $pickup_branchcode} );
     }
 
     if ( $on_shelf_holds == 1 ) {
@@ -1198,6 +1246,8 @@ sub IsAvailableForItemLevelRequest {
         foreach my $i (@items) {
             my $reserves_control_branch = GetReservesControlBranch( $i->unblessed(), $patron->unblessed );
             my $branchitemrule = C4::Circulation::GetBranchItemRule( $reserves_control_branch, $i->itype );
+            my $item_library = Koha::Libraries->find( {branchcode => $i->homebranch} );
+
 
             $any_available = 1
               unless $i->itemlost
@@ -1208,7 +1258,8 @@ sub IsAvailableForItemLevelRequest {
               || ( $i->damaged
                 && !C4::Context->preference('AllowHoldsOnDamagedItems') )
               || Koha::ItemTypes->find( $i->effective_itemtype() )->notforloan
-              || $branchitemrule->{holdallowed} == 1 && $patron->branchcode ne $i->homebranch;
+              || $branchitemrule->{holdallowed} == 1 && $patron->branchcode ne $i->homebranch
+              || $branchitemrule->{holdallowed} == 3 && !$item_library->validate_hold_sibling( {branchcode => $patron->branchcode} );
         }
 
         return $any_available ? 0 : 1;
@@ -1424,7 +1475,7 @@ sub _FixPriority {
     if ( $rank eq "del" ) { # FIXME will crash if called without $hold
         $hold->cancel;
     }
-    elsif ( $rank eq "W" || $rank eq "0" ) {
+    elsif ( $reserve_id && ( $rank eq "W" || $rank eq "0" ) ) {
 
         # make sure priority for waiting or in-transit items is 0
         my $query = "
@@ -1452,11 +1503,12 @@ sub _FixPriority {
         push( @priority,     $line );
     }
 
+    # FIXME This whole sub must be rewritten, especially to highlight what is done when reserve_id is not given
     # To find the matching index
     my $i;
     my $key = -1;    # to allow for 0 to be a valid result
     for ( $i = 0 ; $i < @priority ; $i++ ) {
-        if ( $reserve_id == $priority[$i]->{'reserve_id'} ) {
+        if ( $reserve_id && $reserve_id == $priority[$i]->{'reserve_id'} ) {
             $key = $i;    # save the index
             last;
         }
@@ -1511,6 +1563,13 @@ C<@results> is an array of references-to-hash whose keys are mostly
 fields from the reserves table of the Koha database, plus
 C<biblioitemnumber>.
 
+This routine with either return:
+1 - Item specific holds from the holds queue
+2 - Title level holds from the holds queue
+3 - All holds for this biblionumber
+
+All return values will respect any borrowernumbers passed as arrayref in $ignore_borrowers
+
 =cut
 
 sub _Findgroupreserve {
@@ -1779,7 +1838,7 @@ sub MoveReserve {
     my ( $itemnumber, $borrowernumber, $cancelreserve ) = @_;
 
     my $lookahead = C4::Context->preference('ConfirmFutureHolds'); #number of days to look for future holds
-    my ( $restype, $res, $all_reserves ) = CheckReserves( $itemnumber, undef, $lookahead );
+    my ( $restype, $res, undef ) = CheckReserves( $itemnumber, undef, $lookahead );
     return unless $res;
 
     my $biblionumber     =  $res->{biblionumber};