View | Details | Raw Unified | Return to bug 19532
Collapse All | Expand All

(-)a/C4/Circulation.pm (-2 / +119 lines)
Lines 305-310 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
305
305
306
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
306
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
307
307
308
=item C<RecallPlacedAtHoldingBranch>
309
310
A recall for this item was found, and the transfer has already been completed as the item's branch matches the recall's pickup branch.
311
312
=item C<RecallFound>
313
314
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
315
308
=back
316
=back
309
317
310
=back
318
=back
Lines 378-383 sub transferbook { Link Here
378
        $dotransfer = 1;
386
        $dotransfer = 1;
379
    }
387
    }
380
388
389
    # find recall
390
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
391
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
392
        # do a transfer if the recall branch is different to the item holding branch
393
        if ( $recall->branchcode eq $fbr ) {
394
            $dotransfer = 0;
395
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
396
        } else {
397
            $dotransfer = 1;
398
            $messages->{'RecallFound'} = $recall;
399
        }
400
    }
401
381
    #actually do the transfer....
402
    #actually do the transfer....
382
    if ($dotransfer) {
403
    if ($dotransfer) {
383
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
404
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 724-729 sticky due date is invalid or due date in the past Link Here
724
745
725
if the borrower borrows to much things
746
if the borrower borrows to much things
726
747
748
=head3 RECALLED
749
750
recalled by someone else
751
727
=cut
752
=cut
728
753
729
sub CanBookBeIssued {
754
sub CanBookBeIssued {
Lines 1097-1103 sub CanBookBeIssued { Link Here
1097
        }
1122
        }
1098
    }
1123
    }
1099
1124
1100
    unless ( $ignore_reserves ) {
1125
    my $recall;
1126
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1127
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1128
    # Don't look at recalls that are in transit
1129
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1130
        my @recalls = $biblio->recalls;
1131
1132
        foreach my $r ( @recalls ) {
1133
            if ( $r->itemnumber and
1134
                $r->itemnumber == $item_object->itemnumber and
1135
                $r->borrowernumber == $patron->borrowernumber and
1136
                $r->waiting ) {
1137
                $messages{RECALLED} = $r->recall_id;
1138
                $recall = $r;
1139
                # this item is already waiting for this borrower and the recall can be fulfilled
1140
                last;
1141
            }
1142
            elsif ( $r->itemnumber and
1143
                $r->itemnumber == $item_object->itemnumber and
1144
                $r->in_transit ) {
1145
                # recalled item is in transit
1146
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1147
            }
1148
            elsif ( $r->item_level_recall and
1149
                $r->itemnumber == $item_object->itemnumber and
1150
                $r->borrowernumber != $patron->borrowernumber and
1151
                !$r->in_transit ) {
1152
                # this specific item has been recalled by a different patron
1153
                $needsconfirmation{RECALLED} = $r;
1154
                $recall = $r;
1155
                last;
1156
            }
1157
            elsif ( !$r->item_level_recall and
1158
                $r->borrowernumber != $patron->borrowernumber and
1159
                !$r->in_transit ) {
1160
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1161
                $needsconfirmation{RECALLED} = $r;
1162
                $recall = $r;
1163
                last;
1164
            }
1165
        }
1166
    }
1167
1168
    unless ( $ignore_reserves and defined $recall ) {
1101
        # See if the item is on reserve.
1169
        # See if the item is on reserve.
1102
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1170
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1103
        if ($restype) {
1171
        if ($restype) {
Lines 1390-1395 AddIssue does the following things : Link Here
1390
          * RESERVE PLACED ?
1458
          * RESERVE PLACED ?
1391
              - fill reserve if reserve to this patron
1459
              - fill reserve if reserve to this patron
1392
              - cancel reserve or not, otherwise
1460
              - cancel reserve or not, otherwise
1461
          * RECALL PLACED ?
1462
              - fill recall if recall to this patron
1463
              - cancel recall or not
1464
              - revert recall's waiting status or not
1393
          * TRANSFERT PENDING ?
1465
          * TRANSFERT PENDING ?
1394
              - complete the transfert
1466
              - complete the transfert
1395
          * ISSUE THE BOOK
1467
          * ISSUE THE BOOK
Lines 1404-1409 sub AddIssue { Link Here
1404
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1476
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1405
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1477
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1406
    my $auto_renew = $params && $params->{auto_renew};
1478
    my $auto_renew = $params && $params->{auto_renew};
1479
    my $cancel_recall = $params && $params->{cancel_recall};
1480
    my $recall_id = $params && $params->{recall_id};
1407
    my $dbh          = C4::Context->dbh;
1481
    my $dbh          = C4::Context->dbh;
1408
    my $barcodecheck = CheckValidBarcode($barcode);
1482
    my $barcodecheck = CheckValidBarcode($barcode);
1409
1483
Lines 1475-1480 sub AddIssue { Link Here
1475
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1549
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1476
            }
1550
            }
1477
1551
1552
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1553
1478
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1554
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1479
1555
1480
            # Starting process for transfer job (checking transfert and validate it if we have one)
1556
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1922-1927 Value 1 if return is successful. Link Here
1922
1998
1923
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1999
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1924
2000
2001
=item C<RecallFound>
2002
2003
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2004
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2005
branchcode.
2006
2007
=item C<TransferredRecall>
2008
2009
This item has been transferred to this branch to fill a recall. The recall object is returned.
2010
1925
=back
2011
=back
1926
2012
1927
C<$iteminformation> is a reference-to-hash, giving information about the
2013
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2178-2183 sub AddReturn { Link Here
2178
        }
2264
        }
2179
    }
2265
    }
2180
2266
2267
    # find recalls...
2268
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2269
    my $recall = undef;
2270
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2271
    if ( defined $recall ) {
2272
        $messages->{RecallFound} = $recall;
2273
        if ( $recall->branchcode ne $branch ) {
2274
            $messages->{RecallNeedsTransfer} = $branch;
2275
        }
2276
    }
2277
2181
    # find reserves.....
2278
    # find reserves.....
2182
    # launch the Checkreserves routine to find any holds
2279
    # launch the Checkreserves routine to find any holds
2183
    my ($resfound, $resrec);
2280
    my ($resfound, $resrec);
Lines 2236-2243 sub AddReturn { Link Here
2236
        $request->status('RET') if $request;
2333
        $request->status('RET') if $request;
2237
    }
2334
    }
2238
2335
2336
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2337
    if ( $transfer_recall and
2338
         $transfer_recall->branchcode eq $branch and
2339
         C4::Context->preference('UseRecalls') ) {
2340
        $messages->{TransferredRecall} = $transfer_recall;
2341
    }
2342
2239
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2343
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2240
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
2344
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch)
2345
 and not $messages->{'WrongTransfer'} and not $messages->{TransferredRecall} and not $messages->{RecallNeedsTransfer} ){
2241
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2346
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2242
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2347
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2243
            (C4::Context->preference("UseBranchTransferLimits") and
2348
            (C4::Context->preference("UseBranchTransferLimits") and
Lines 2824-2829 sub CanBookBeRenewed { Link Here
2824
        }
2929
        }
2825
    }
2930
    }
2826
2931
2932
    my $recall = undef;
2933
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2934
    if ( defined $recall ) {
2935
        if ( $recall->item_level_recall ) {
2936
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2937
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2938
        } else {
2939
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2940
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2941
        }
2942
    }
2943
2827
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2944
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2828
2945
2829
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2946
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
(-)a/C4/Reserves.pm (+5 lines)
Lines 362-367 sub CanBookBeReserved{ Link Here
362
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
362
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
363
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
363
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
364
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
364
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
365
         { status => recall }, if the borrower has already placed a recall on this item
365
366
366
=cut
367
=cut
367
368
Lines 399-404 sub CanItemBeReserved { Link Here
399
        return { status =>'alreadypossession' };
400
        return { status =>'alreadypossession' };
400
    }
401
    }
401
402
403
    # check if a recall exists on this item from this borrower
404
    return { status => 'recall' }
405
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
406
402
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
407
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
403
408
404
    my $querycount = q{
409
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1894-1899 sub searchResults { Link Here
1894
        my $can_place_holds       = 0;
1894
        my $can_place_holds       = 0;
1895
        my $item_onhold_count     = 0;
1895
        my $item_onhold_count     = 0;
1896
        my $notforloan_count      = 0;
1896
        my $notforloan_count      = 0;
1897
        my $item_recalled_count   = 0;
1897
        my $items_count           = scalar(@fields);
1898
        my $items_count           = scalar(@fields);
1898
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1899
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1899
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1900
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1988-1993 sub searchResults { Link Here
1988
                # is item on the reserve shelf?
1989
                # is item on the reserve shelf?
1989
                my $reservestatus = '';
1990
                my $reservestatus = '';
1990
1991
1992
                # is item a waiting recall?
1993
                my $recallstatus = '';
1994
1991
                unless ($item->{withdrawn}
1995
                unless ($item->{withdrawn}
1992
                        || $item->{itemlost}
1996
                        || $item->{itemlost}
1993
                        || $item->{damaged}
1997
                        || $item->{damaged}
Lines 2009-2014 sub searchResults { Link Here
2009
                    #
2013
                    #
2010
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2014
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2011
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2015
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2016
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
2012
                }
2017
                }
2013
2018
2014
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
2019
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 2017-2022 sub searchResults { Link Here
2017
                    || $item->{damaged}
2022
                    || $item->{damaged}
2018
                    || $item->{notforloan}
2023
                    || $item->{notforloan}
2019
                    || $reservestatus eq 'Waiting'
2024
                    || $reservestatus eq 'Waiting'
2025
                    || $recallstatus eq 'Waiting'
2020
                    || ($transfertwhen && $transfertwhen ne ''))
2026
                    || ($transfertwhen && $transfertwhen ne ''))
2021
                {
2027
                {
2022
                    $withdrawn_count++        if $item->{withdrawn};
2028
                    $withdrawn_count++        if $item->{withdrawn};
Lines 2024-2029 sub searchResults { Link Here
2024
                    $itemdamaged_count++     if $item->{damaged};
2030
                    $itemdamaged_count++     if $item->{damaged};
2025
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2031
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2026
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2032
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2033
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
2027
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2034
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2028
2035
2029
                    # can place a hold on a item if
2036
                    # can place a hold on a item if
Lines 2045-2050 sub searchResults { Link Here
2045
                        $other_items->{$key}->{$_} = $item->{$_};
2052
                        $other_items->{$key}->{$_} = $item->{$_};
2046
                    }
2053
                    }
2047
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2054
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2055
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
2048
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2056
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2049
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2057
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2050
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
2058
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2119-2124 sub searchResults { Link Here
2119
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2127
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2120
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2128
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2121
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2129
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2130
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2122
        $oldbiblio->{orderedcount}         = $ordered_count;
2131
        $oldbiblio->{orderedcount}         = $ordered_count;
2123
        $oldbiblio->{notforloancount}      = $notforloan_count;
2132
        $oldbiblio->{notforloancount}      = $notforloan_count;
2124
2133
(-)a/C4/XSLT.pm (-1 / +7 lines)
Lines 321-327 sub buildKohaItemsNamespace { Link Here
321
        my $status;
321
        my $status;
322
        my $substatus = '';
322
        my $substatus = '';
323
323
324
        if ($item->has_pending_hold) {
324
        my $recalls = Koha::Recalls->search({ itemnumber => $item->itemnumber, status => 'W' });
325
326
        if ( $recalls->count ) {
327
            # recalls take priority over holds
328
            $status = 'Waiting';
329
        }
330
        elsif ( $item->has_pending_hold ) {
325
            $status = 'Pending hold';
331
            $status = 'Pending hold';
326
        }
332
        }
327
        elsif ( $item->holds->waiting->count ) {
333
        elsif ( $item->holds->waiting->count ) {
(-)a/Koha/Biblio.pm (+108 lines)
Lines 835-840 sub to_api_mapping { Link Here
835
    };
835
    };
836
}
836
}
837
837
838
=head3 recalls
839
840
    my @recalls = $biblio->recalls;
841
842
Return all active recalls attached to this biblio, sorted by oldest first
843
844
=cut
845
846
sub recalls {
847
    my ( $self ) = @_;
848
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
849
    return @recalls_rs;
850
}
851
852
=head3 can_be_recalled
853
854
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
855
856
Does biblio-level checks and returns the items attached to this biblio that are available for recall
857
858
=cut
859
860
sub can_be_recalled {
861
    my ( $self, $params ) = @_;
862
863
    return 0 if !( C4::Context->preference('UseRecalls') );
864
865
    my $patron = $params->{patron};
866
867
    my $branchcode = C4::Context->userenv->{'branch'};
868
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
869
        $branchcode = $patron->branchcode;
870
    }
871
872
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
873
874
    # if there are no available items at all, no recall can be placed
875
    return 0 if ( scalar @all_items == 0 );
876
877
    my @itemtypes;
878
    my @itemnumbers;
879
    my @items;
880
    foreach my $item ( @all_items ) {
881
        if ( $item->can_be_recalled({ patron => $patron }) ) {
882
            push( @itemtypes, $item->effective_itemtype );
883
            push( @itemnumbers, $item->itemnumber );
884
            push( @items, $item );
885
        }
886
    }
887
888
    # if there are no recallable items, no recall can be placed
889
    return 0 if ( scalar @items == 0 );
890
891
    # Check the circulation rule for each relevant itemtype for this biblio
892
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
893
    foreach my $itemtype ( @itemtypes ) {
894
        my $rule = Koha::CirculationRules->get_effective_rules({
895
            branchcode => $branchcode,
896
            categorycode => $patron ? $patron->categorycode : undef,
897
            itemtype => $itemtype,
898
            rules => [
899
                'recalls_allowed',
900
                'recalls_per_record',
901
                'on_shelf_recalls',
902
            ],
903
        });
904
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
905
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
906
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
907
    }
908
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
909
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
910
    my %on_shelf_recalls_count = ();
911
    foreach my $count ( @on_shelf_recalls ) {
912
        $on_shelf_recalls_count{$count}++;
913
    }
914
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
915
916
    # check recalls allowed has been set and is not zero
917
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
918
919
    if ( $patron ) {
920
        # check borrower has not reached open recalls allowed limit
921
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
922
923
        # check borrower has not reached open recalls allowed per record limit
924
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
925
926
        # check if any of the items under this biblio are already checked out by this borrower
927
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
928
    }
929
930
    # check item availability
931
    my $checked_out_count = 0;
932
    foreach (@items) {
933
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
934
    }
935
936
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
937
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
938
939
    # can't recall if no items have been checked out
940
    return 0 if ( $checked_out_count == 0 );
941
942
    # can recall
943
    return @items;
944
}
945
838
=head2 Internal methods
946
=head2 Internal methods
839
947
840
=head3 type
948
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1076-1081 sub _after_item_action_hooks { Link Here
1076
    );
1076
    );
1077
}
1077
}
1078
1078
1079
=head3 recall
1080
1081
    my $recall = $item->recall;
1082
1083
Return the relevant recall for this item
1084
1085
=cut
1086
1087
sub recall {
1088
    my ( $self ) = @_;
1089
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1090
    foreach my $recall (@recalls) {
1091
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1092
            return $recall;
1093
        }
1094
    }
1095
    # no item-level recall to return, so return earliest biblio-level
1096
    # FIXME: eventually this will be based on priority
1097
    return $recalls[0];
1098
}
1099
1100
=head3 can_be_recalled
1101
1102
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1103
1104
Does item-level checks and returns if items can be recalled by this borrower
1105
1106
=cut
1107
1108
sub can_be_recalled {
1109
    my ( $self, $params ) = @_;
1110
1111
    return 0 if !( C4::Context->preference('UseRecalls') );
1112
1113
    # check if this item is not for loan, withdrawn or lost
1114
    return 0 if ( $self->notforloan != 0 );
1115
    return 0 if ( $self->itemlost != 0 );
1116
    return 0 if ( $self->withdrawn != 0 );
1117
1118
    # check if this item is not checked out - if not checked out, can't be recalled
1119
    return 0 if ( !defined( $self->checkout ) );
1120
1121
    my $patron = $params->{patron};
1122
1123
    my $branchcode = C4::Context->userenv->{'branch'};
1124
    if ( $patron ) {
1125
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1126
    }
1127
1128
    # Check the circulation rule for each relevant itemtype for this item
1129
    my $rule = Koha::CirculationRules->get_effective_rules({
1130
        branchcode => $branchcode,
1131
        categorycode => $patron ? $patron->categorycode : undef,
1132
        itemtype => $self->effective_itemtype,
1133
        rules => [
1134
            'recalls_allowed',
1135
            'recalls_per_record',
1136
            'on_shelf_recalls',
1137
        ],
1138
    });
1139
1140
    # check recalls allowed has been set and is not zero
1141
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1142
1143
    if ( $patron ) {
1144
        # check borrower has not reached open recalls allowed limit
1145
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1146
1147
        # check borrower has not reach open recalls allowed per record limit
1148
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1149
1150
        # check if this patron has already recalled this item
1151
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1152
1153
        # check if this patron has already checked out this item
1154
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1155
1156
        # check if this patron has already reserved this item
1157
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1158
    }
1159
1160
    # check item availability
1161
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1162
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1163
1164
    # if there are no available items at all, no recall can be placed
1165
    return 0 if ( scalar @items == 0 );
1166
1167
    my $checked_out_count = 0;
1168
    foreach (@items) {
1169
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1170
    }
1171
1172
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1173
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1174
1175
    # can't recall if no items have been checked out
1176
    return 0 if ( $checked_out_count == 0 );
1177
1178
    # can recall
1179
    return 1;
1180
}
1181
1182
=head3 can_be_waiting_recall
1183
1184
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1185
1186
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1187
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1188
1189
=cut
1190
1191
sub can_be_waiting_recall {
1192
    my ( $self ) = @_;
1193
1194
    return 0 if !( C4::Context->preference('UseRecalls') );
1195
1196
    # check if this item is not for loan, withdrawn or lost
1197
    return 0 if ( $self->notforloan != 0 );
1198
    return 0 if ( $self->itemlost != 0 );
1199
    return 0 if ( $self->withdrawn != 0 );
1200
1201
    my $branchcode = $self->holdingbranch;
1202
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1203
        $branchcode = C4::Context->userenv->{'branch'};
1204
    } else {
1205
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1206
    }
1207
1208
    # Check the circulation rule for each relevant itemtype for this item
1209
    my $rule = Koha::CirculationRules->get_effective_rules({
1210
        branchcode => $branchcode,
1211
        categorycode => undef,
1212
        itemtype => $self->effective_itemtype,
1213
        rules => [
1214
            'recalls_allowed',
1215
        ],
1216
    });
1217
1218
    # check recalls allowed has been set and is not zero
1219
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1220
1221
    # can recall
1222
    return 1;
1223
}
1224
1225
=head3 check_recalls
1226
1227
    my $recall = $item->check_recalls;
1228
1229
Get the most relevant recall for this item.
1230
1231
=cut
1232
1233
sub check_recalls {
1234
    my ( $self ) = @_;
1235
1236
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1237
1238
    my $recall;
1239
    # iterate through relevant recalls to find the best one.
1240
    # if we come across a waiting recall, use this one.
1241
    # if we have iterated through all recalls and not found a waiting recall, use the first recall in the array, which should be the oldest recall.
1242
    foreach my $r ( @recalls ) {
1243
        if ( $r->waiting ) {
1244
            $recall = $r;
1245
            last;
1246
        }
1247
    }
1248
    unless ( defined $recall ) {
1249
        $recall = $recalls[0];
1250
    }
1251
1252
    return $recall;
1253
}
1254
1079
=head3 _type
1255
=head3 _type
1080
1256
1081
=cut
1257
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1785-1790 sub to_api_mapping { Link Here
1785
    };
1785
    };
1786
}
1786
}
1787
1787
1788
=head3 recalls
1789
1790
    my $recalls = $patron->recalls;
1791
1792
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1793
1794
Return the patron's active recalls - total, or on a specific biblio
1795
1796
=cut
1797
1798
sub recalls {
1799
    my ( $self, $params ) = @_;
1800
1801
    my $biblionumber = $params->{biblionumber};
1802
1803
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1804
1805
    if ( $biblionumber ) {
1806
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1807
    }
1808
1809
    return $recalls_rs;
1810
}
1811
1788
=head2 Internal methods
1812
=head2 Internal methods
1789
1813
1790
=head3 _type
1814
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 27-32 use Koha::Biblios; Link Here
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::ArticleRequest::Status;
29
use Koha::ArticleRequest::Status;
30
use Koha::Recalls;
30
31
31
sub HoldsCount {
32
sub HoldsCount {
32
    my ( $self, $biblionumber ) = @_;
33
    my ( $self, $biblionumber ) = @_;
Lines 63-66 sub CanArticleRequest { Link Here
63
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
}
65
}
65
66
67
sub RecallsCount {
68
    my ( $self, $biblionumber ) = @_;
69
70
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
71
72
    return $recalls->count;
73
}
74
66
1;
75
1;
(-)a/t/db_dependent/Circulation.t (-2 / +259 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
use utf8;
19
use utf8;
20
20
21
use Test::More tests => 52;
21
use Test::More tests => 55;
22
use Test::Exception;
22
use Test::Exception;
23
use Test::MockModule;
23
use Test::MockModule;
24
use Test::Deep qw( cmp_deeply );
24
use Test::Deep qw( cmp_deeply );
Lines 329-335 subtest "GetIssuingCharges tests" => sub { Link Here
329
329
330
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
330
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
331
subtest "CanBookBeRenewed tests" => sub {
331
subtest "CanBookBeRenewed tests" => sub {
332
    plan tests => 89;
332
    plan tests => 93;
333
333
334
    C4::Context->set_preference('ItemsDeniedRenewal','');
334
    C4::Context->set_preference('ItemsDeniedRenewal','');
335
    # Generate test biblio
335
    # Generate test biblio
Lines 1338-1343 subtest "CanBookBeRenewed tests" => sub { Link Here
1338
            $item_3->itemcallnumber || '' ),
1338
            $item_3->itemcallnumber || '' ),
1339
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1339
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1340
    );
1340
    );
1341
1342
    # Recalls
1343
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1344
    Koha::CirculationRules->set_rules({
1345
        categorycode => undef,
1346
        branchcode => undef,
1347
        itemtype => undef,
1348
        rules => {
1349
            recalls_allowed => 10,
1350
            renewalsallowed => 5,
1351
        },
1352
    });
1353
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1354
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1355
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1356
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1357
1358
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1359
1360
    # item-level and this item: renewal not allowed
1361
    my $recall = Koha::Recall->new({
1362
        biblionumber => $recall_item1->biblionumber,
1363
        itemnumber => $recall_item1->itemnumber,
1364
        borrowernumber => $recall_borrower->borrowernumber,
1365
        branchcode => $recall_borrower->branchcode,
1366
        item_level_recall => 1,
1367
        status => 'R',
1368
    })->store;
1369
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1370
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1371
    $recall->set_cancelled;
1372
1373
    # biblio-level requested recall: renewal not allowed
1374
    $recall = Koha::Recall->new({
1375
        biblionumber => $recall_item1->biblionumber,
1376
        itemnumber => undef,
1377
        borrowernumber => $recall_borrower->borrowernumber,
1378
        branchcode => $recall_borrower->branchcode,
1379
        item_level_recall => 0,
1380
        status => 'R',
1381
    })->store;
1382
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1383
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1384
    $recall->set_cancelled;
1385
1386
    # item-level and not this item: renewal allowed
1387
    $recall = Koha::Recall->new({
1388
        biblionumber => $recall_item2->biblionumber,
1389
        itemnumber => $recall_item2->itemnumber,
1390
        borrowernumber => $recall_borrower->borrowernumber,
1391
        branchcode => $recall_borrower->branchcode,
1392
        item_level_recall => 1,
1393
        status => 'R',
1394
    })->store;
1395
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1396
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1397
    $recall->set_cancelled;
1398
1399
    # biblio-level waiting recall: renewal allowed
1400
    $recall = Koha::Recall->new({
1401
        biblionumber => $recall_item1->biblionumber,
1402
        itemnumber => undef,
1403
        borrowernumber => $recall_borrower->borrowernumber,
1404
        branchcode => $recall_borrower->branchcode,
1405
        item_level_recall => 0,
1406
        status => 'R',
1407
    })->store;
1408
    $recall->set_waiting({ item => $recall_item1 });
1409
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1410
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1411
    $recall->set_cancelled;
1341
};
1412
};
1342
1413
1343
subtest "GetUpcomingDueIssues" => sub {
1414
subtest "GetUpcomingDueIssues" => sub {
Lines 1818-1823 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1818
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1889
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1819
};
1890
};
1820
1891
1892
subtest 'AddIssue | recalls' => sub {
1893
    plan tests => 3;
1894
1895
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1896
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1897
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1898
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1899
    my $item = $builder->build_sample_item;
1900
    Koha::CirculationRules->set_rules({
1901
        branchcode => undef,
1902
        itemtype => undef,
1903
        categorycode => undef,
1904
        rules => {
1905
            recalls_allowed => 10,
1906
        },
1907
    });
1908
1909
    # checking out item that they have recalled
1910
    my $recall1 = Koha::Recall->new({
1911
        borrowernumber => $patron1->borrowernumber,
1912
        biblionumber => $item->biblionumber,
1913
        itemnumber => $item->itemnumber,
1914
        item_level_recall => 1,
1915
        branchcode => $patron1->branchcode,
1916
        status => 'R',
1917
    })->store;
1918
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1919
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1920
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1921
    AddReturn( $item->barcode, $item->homebranch );
1922
1923
    # this item is has a recall request. cancel recall
1924
    my $recall2 = Koha::Recall->new({
1925
        borrowernumber => $patron2->borrowernumber,
1926
        biblionumber => $item->biblionumber,
1927
        itemnumber => $item->itemnumber,
1928
        item_level_recall => 1,
1929
        branchcode => $patron2->branchcode,
1930
        status => 'R',
1931
    })->store;
1932
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1933
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1934
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1935
    AddReturn( $item->barcode, $item->homebranch );
1936
1937
    # this item is waiting to fulfill a recall. revert recall
1938
    my $recall3 = Koha::Recall->new({
1939
        borrowernumber => $patron2->borrowernumber,
1940
        biblionumber => $item->biblionumber,
1941
        itemnumber => $item->itemnumber,
1942
        item_level_recall => 1,
1943
        branchcode => $patron2->branchcode,
1944
        status => 'R',
1945
    })->store;
1946
    $recall3->set_waiting;
1947
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1948
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1949
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1950
    AddReturn( $item->barcode, $item->homebranch );
1951
};
1952
1953
1821
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1954
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1822
    plan tests => 8;
1955
    plan tests => 8;
1823
1956
Lines 3721-3726 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3721
    # TODO test with AllowNotForLoanOverride = 1
3854
    # TODO test with AllowNotForLoanOverride = 1
3722
};
3855
};
3723
3856
3857
subtest 'CanBookBeIssued | recalls' => sub {
3858
    plan tests => 3;
3859
3860
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3861
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3862
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3863
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3864
    my $item = $builder->build_sample_item;
3865
    Koha::CirculationRules->set_rules({
3866
        branchcode => undef,
3867
        itemtype => undef,
3868
        categorycode => undef,
3869
        rules => {
3870
            recalls_allowed => 10,
3871
        },
3872
    });
3873
3874
    # item-level recall
3875
    my $recall = Koha::Recall->new({
3876
        borrowernumber => $patron1->borrowernumber,
3877
        biblionumber => $item->biblionumber,
3878
        itemnumber => $item->itemnumber,
3879
        item_level_recall => 1,
3880
        branchcode => $patron1->branchcode,
3881
        status => 'R',
3882
    })->store;
3883
3884
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3885
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3886
3887
    $recall->set_cancelled;
3888
3889
    # biblio-level recall
3890
    $recall = Koha::Recall->new({
3891
        borrowernumber => $patron1->borrowernumber,
3892
        biblionumber => $item->biblionumber,
3893
        itemnumber => undef,
3894
        item_level_recall => 0,
3895
        branchcode => $patron1->branchcode,
3896
        status => 'R',
3897
    })->store;
3898
3899
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3900
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3901
3902
    $recall->set_cancelled;
3903
3904
    # biblio-level recall
3905
    $recall = Koha::Recall->new({
3906
        borrowernumber => $patron1->borrowernumber,
3907
        biblionumber => $item->biblionumber,
3908
        itemnumber => undef,
3909
        item_level_recall => 0,
3910
        branchcode => $patron1->branchcode,
3911
        status => 'R',
3912
    })->store;
3913
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
3914
3915
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
3916
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
3917
3918
    $recall->set_cancelled;
3919
};
3920
3724
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3921
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3725
    plan tests => 1;
3922
    plan tests => 1;
3726
3923
Lines 3736-3741 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3736
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3933
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3737
};
3934
};
3738
3935
3936
subtest 'AddReturn | recalls' => sub {
3937
    plan tests => 3;
3938
3939
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3940
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3941
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3942
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3943
    my $item1 = $builder->build_sample_item;
3944
    Koha::CirculationRules->set_rules({
3945
        branchcode => undef,
3946
        itemtype => undef,
3947
        categorycode => undef,
3948
        rules => {
3949
            recalls_allowed => 10,
3950
        },
3951
    });
3952
3953
    # this item can fill a recall with pickup at this branch
3954
    AddIssue( $patron1->unblessed, $item1->barcode );
3955
    my $recall1 = Koha::Recall->new({
3956
        borrowernumber => $patron2->borrowernumber,
3957
        biblionumber => $item1->biblionumber,
3958
        itemnumber => $item1->itemnumber,
3959
        item_level_recall => 1,
3960
        branchcode => $item1->homebranch,
3961
        status => 'R',
3962
    })->store;
3963
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3964
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3965
    $recall1->set_cancelled;
3966
3967
    # this item can fill a recall but needs transfer
3968
    AddIssue( $patron1->unblessed, $item1->barcode );
3969
    $recall1 = Koha::Recall->new({
3970
        borrowernumber => $patron2->borrowernumber,
3971
        biblionumber => $item1->biblionumber,
3972
        itemnumber => $item1->itemnumber,
3973
        item_level_recall => 1,
3974
        branchcode => $patron2->branchcode,
3975
        status => 'R',
3976
    })->store;
3977
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3978
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3979
    $recall1->set_cancelled;
3980
3981
    # this item is already in transit, do not ask to transfer
3982
    AddIssue( $patron1->unblessed, $item1->barcode );
3983
    $recall1 = Koha::Recall->new({
3984
        borrowernumber => $patron2->borrowernumber,
3985
        biblionumber => $item1->biblionumber,
3986
        itemnumber => $item1->itemnumber,
3987
        item_level_recall => 1,
3988
        branchcode => $patron2->branchcode,
3989
        status => 'R',
3990
    })->store;
3991
    $recall1->start_transfer;
3992
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3993
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3994
    $recall1->set_cancelled;
3995
};
3739
3996
3740
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3997
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3741
3998
(-)a/t/db_dependent/Circulation/transferbook.t (-1 / +32 lines)
Lines 27-32 use Koha::DateUtils qw( dt_from_string ); Link Here
27
use Koha::Item::Transfers;
27
use Koha::Item::Transfers;
28
28
29
my $builder = t::lib::TestBuilder->new;
29
my $builder = t::lib::TestBuilder->new;
30
my $schema = Koha::Database->new->schema;
31
32
$schema->storage->txn_begin;
30
33
31
subtest 'transfer a non-existant item' => sub {
34
subtest 'transfer a non-existant item' => sub {
32
    plan tests => 2;
35
    plan tests => 2;
Lines 99-105 subtest 'field population tests' => sub { Link Here
99
#FIXME:'UseBranchTransferLimits tests missing
102
#FIXME:'UseBranchTransferLimits tests missing
100
103
101
subtest 'transfer already at destination' => sub {
104
subtest 'transfer already at destination' => sub {
102
    plan tests => 5;
105
    plan tests => 9;
103
106
104
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
107
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
105
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
108
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
Lines 147-152 subtest 'transfer already at destination' => sub { Link Here
147
    is( $dotransfer, 1, 'Transfer of reserved item succeeded without ignore reserves' );
150
    is( $dotransfer, 1, 'Transfer of reserved item succeeded without ignore reserves' );
148
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
151
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
149
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
152
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
153
154
    # recalls
155
    t::lib::Mocks::mock_preference('UseRecalls', 1);
156
    my $recall = Koha::Recall->new({
157
        biblionumber => $item->biblionumber,
158
        itemnumber => $item->itemnumber,
159
        item_level_recall => 1,
160
        borrowernumber => $patron->borrowernumber,
161
        branchcode => $library->branchcode,
162
        status => 'R',
163
    })->store;
164
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
165
    is( $dotransfer, 0, 'Do not transfer recalled item, it has already arrived' );
166
    is( $messages->{RecallPlacedAtHoldingBranch}, 1, "We found the recall");
167
168
    my $item2 = $builder->build_object({ class => 'Koha::Items' }); # this item will have a different holding branch to the pickup branch
169
    $recall = Koha::Recall->new({
170
        biblionumber => $item2->biblionumber,
171
        itemnumber => $item2->itemnumber,
172
        item_level_recall => 1,
173
        borrowernumber => $patron->borrowernumber,
174
        branchcode => $library->branchcode,
175
        status => 'R',
176
    })->store;
177
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
178
    is( $dotransfer, 1, 'Transfer of recalled item succeeded' );
179
    is( $messages->{RecallFound}->recall_id, $recall->recall_id, "We found the recall");
150
};
180
};
151
181
152
subtest 'transfer an issued item' => sub {
182
subtest 'transfer an issued item' => sub {
Lines 294-296 subtest 'transferbook test from branch' => sub { Link Here
294
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
324
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
295
325
296
};
326
};
327
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (+27 lines)
Lines 1326-1332 subtest 'non priority holds' => sub { Link Here
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1327
1327
1328
    $schema->storage->txn_rollback;
1328
    $schema->storage->txn_rollback;
1329
};
1330
1331
subtest 'CanItemBeReserved / recall' => sub {
1332
    plan tests => 1;
1333
1334
    $schema->storage->txn_begin;
1335
1336
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1337
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1338
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1339
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1340
    my $item1   = $builder->build_sample_item(
1341
        {
1342
            biblionumber => $biblio1->biblionumber,
1343
            library      => $library1->branchcode
1344
        }
1345
    );
1346
    Koha::Recall->new({
1347
        borrowernumber => $patron1->borrowernumber,
1348
        biblionumber => $biblio1->biblionumber,
1349
        branchcode => $library1->branchcode,
1350
        itemnumber => $item1->itemnumber,
1351
        recalldate => '2020-05-04 10:10:10',
1352
        item_level_recall => 1,
1353
    })->store;
1354
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1329
1355
1356
    $schema->storage->txn_rollback;
1330
};
1357
};
1331
1358
1332
subtest 'CanItemBeReserved rule precedence tests' => sub {
1359
subtest 'CanItemBeReserved rule precedence tests' => sub {
(-)a/t/db_dependent/Koha/Biblio.t (-1 / +118 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 12;
20
use Test::More tests => 13;
21
21
22
use C4::Biblio;
22
use C4::Biblio;
23
use Koha::Database;
23
use Koha::Database;
Lines 551-553 subtest 'subscriptions() tests' => sub { Link Here
551
551
552
    $schema->storage->txn_rollback;
552
    $schema->storage->txn_rollback;
553
};
553
};
554
555
subtest 'Recalls tests' => sub {
556
557
    plan tests => 12;
558
559
    $schema->storage->txn_begin;
560
    my $item1 = $builder->build_sample_item;
561
    my $biblio = $item1->biblio;
562
    my $branchcode = $item1->holdingbranch;
563
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
564
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
565
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
566
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
567
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
568
569
    my $recall1 = Koha::Recall->new({
570
        borrowernumber => $patron1->borrowernumber,
571
        recalldate => Koha::DateUtils::dt_from_string,
572
        biblionumber => $biblio->biblionumber,
573
        branchcode => $branchcode,
574
        status => 'R',
575
        itemnumber => $item1->itemnumber,
576
        expirationdate => undef,
577
        item_level_recall => 1
578
    })->store;
579
    my $recall2 = Koha::Recall->new({
580
        borrowernumber => $patron2->borrowernumber,
581
        recalldate => Koha::DateUtils::dt_from_string,
582
        biblionumber => $biblio->biblionumber,
583
        branchcode => $branchcode,
584
        status => 'R',
585
        itemnumber => undef,
586
        expirationdate => undef,
587
        item_level_recall => 0
588
    })->store;
589
    my $recall3 = Koha::Recall->new({
590
        borrowernumber => $patron3->borrowernumber,
591
        recalldate => Koha::DateUtils::dt_from_string,
592
        biblionumber => $biblio->biblionumber,
593
        branchcode => $branchcode,
594
        status => 'R',
595
        itemnumber => $item1->itemnumber,
596
        expirationdate => undef,
597
        item_level_recall => 1
598
    })->store;
599
600
    my $recalls_count = scalar $biblio->recalls;
601
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
602
603
    $recall1->set_cancelled;
604
    $recall2->set_expired({ interface => 'COMMANDLINE' });
605
606
    $recalls_count = scalar $biblio->recalls;
607
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
608
609
    t::lib::Mocks::mock_preference('UseRecalls', 0);
610
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
611
612
    t::lib::Mocks::mock_preference("UseRecalls", 1);
613
    $item1->update({ notforloan => 1 });
614
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
615
616
    $item1->update({ notforloan => 0 });
617
    Koha::CirculationRules->set_rules({
618
        branchcode => $branchcode,
619
        categorycode => $patron1->categorycode,
620
        itemtype => $item1->effective_itemtype,
621
        rules => {
622
            recalls_allowed => 0,
623
            recalls_per_record => 1,
624
            on_shelf_recalls => 'all',
625
        },
626
    });
627
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
628
629
    Koha::CirculationRules->set_rules({
630
        branchcode => $branchcode,
631
        categorycode => $patron1->categorycode,
632
        itemtype => $item1->effective_itemtype,
633
        rules => {
634
            recalls_allowed => 1,
635
            recalls_per_record => 1,
636
            on_shelf_recalls => 'all',
637
        },
638
    });
639
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
640
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
641
642
    $recall1->set_cancelled;
643
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
644
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
645
646
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
647
648
    Koha::CirculationRules->set_rules({
649
        branchcode => $branchcode,
650
        categorycode => $patron1->categorycode,
651
        itemtype => $item1->effective_itemtype,
652
        rules => {
653
            recalls_allowed => 1,
654
            recalls_per_record => 1,
655
            on_shelf_recalls => 'any',
656
        },
657
    });
658
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
659
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
660
661
    $recall2->set_cancelled;
662
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
663
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
664
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
665
666
    $item1->update({ withdrawn => 1 });
667
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
668
669
    $schema->storage->txn_rollback;
670
};
(-)a/t/db_dependent/Koha/Item.t (-1 / +188 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 7;
22
use Test::More tests => 8;
23
23
24
use C4::Biblio;
24
use C4::Biblio;
25
use C4::Circulation;
25
use C4::Circulation;
Lines 628-630 subtest 'Tests for itemtype' => sub { Link Here
628
628
629
    $schema->storage->txn_rollback;
629
    $schema->storage->txn_rollback;
630
};
630
};
631
632
subtest 'Recalls tests' => sub {
633
634
    plan tests => 20;
635
636
    $schema->storage->txn_begin;
637
638
    my $item1 = $builder->build_sample_item;
639
    my $biblio = $item1->biblio;
640
    my $branchcode = $item1->holdingbranch;
641
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
642
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
643
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
644
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
645
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
646
647
    my $recall1 = Koha::Recall->new({
648
        borrowernumber => $patron1->borrowernumber,
649
        recalldate => Koha::DateUtils::dt_from_string,
650
        biblionumber => $biblio->biblionumber,
651
        branchcode => $branchcode,
652
        status => 'R',
653
        itemnumber => $item1->itemnumber,
654
        expirationdate => undef,
655
        item_level_recall => 1
656
    })->store;
657
    my $recall2 = Koha::Recall->new({
658
        borrowernumber => $patron2->borrowernumber,
659
        recalldate => Koha::DateUtils::dt_from_string,
660
        biblionumber => $biblio->biblionumber,
661
        branchcode => $branchcode,
662
        status => 'R',
663
        itemnumber => $item1->itemnumber,
664
        expirationdate => undef,
665
        item_level_recall =>1
666
    })->store;
667
668
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
669
670
    $recall2->set_cancelled;
671
672
    t::lib::Mocks::mock_preference('UseRecalls', 0);
673
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
674
675
    t::lib::Mocks::mock_preference("UseRecalls", 1);
676
677
    $item1->update({ notforloan => 1 });
678
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
679
    $item1->update({ notforloan => 0, itemlost => 1 });
680
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
681
    $item1->update({ itemlost => 0, withdrawn => 1 });
682
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
683
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
684
685
    $item1->update({ withdrawn => 0 });
686
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
687
688
    Koha::CirculationRules->set_rules({
689
        branchcode => $branchcode,
690
        categorycode => $patron1->categorycode,
691
        itemtype => $item1->effective_itemtype,
692
        rules => {
693
            recalls_allowed => 0,
694
            recalls_per_record => 1,
695
            on_shelf_recalls => 'all',
696
        },
697
    });
698
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
699
700
    Koha::CirculationRules->set_rules({
701
        branchcode => $branchcode,
702
        categorycode => $patron1->categorycode,
703
        itemtype => $item1->effective_itemtype,
704
        rules => {
705
            recalls_allowed => 1,
706
            recalls_per_record => 1,
707
            on_shelf_recalls => 'all',
708
        },
709
    });
710
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
711
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
712
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
713
714
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
715
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
716
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
717
718
    $recall1->set_cancelled;
719
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
720
721
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
722
723
    Koha::CirculationRules->set_rules({
724
        branchcode => $branchcode,
725
        categorycode => $patron1->categorycode,
726
        itemtype => $item1->effective_itemtype,
727
        rules => {
728
            recalls_allowed => 1,
729
            recalls_per_record => 1,
730
            on_shelf_recalls => 'any',
731
        },
732
    });
733
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
734
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
735
736
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
737
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
738
739
    $recall1 = Koha::Recall->new({
740
        borrowernumber => $patron1->borrowernumber,
741
        recalldate => Koha::DateUtils::dt_from_string,
742
        biblionumber => $biblio->biblionumber,
743
        branchcode => $branchcode,
744
        status => 'R',
745
        itemnumber => undef,
746
        expirationdate => undef,
747
        item_level_recall => 0
748
    })->store;
749
750
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
751
752
    Koha::CirculationRules->set_rules({
753
        branchcode => undef,
754
        categorycode => undef,
755
        itemtype => $item1->effective_itemtype,
756
        rules => {
757
            recalls_allowed => 0,
758
            recalls_per_record => 1,
759
            on_shelf_recalls => 'any',
760
        },
761
    });
762
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
763
764
    Koha::CirculationRules->set_rules({
765
        branchcode => undef,
766
        categorycode => undef,
767
        itemtype => $item1->effective_itemtype,
768
        rules => {
769
            recalls_allowed => 1,
770
            recalls_per_record => 1,
771
            on_shelf_recalls => 'any',
772
        },
773
    });
774
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
775
776
    # check_recalls tests
777
778
    $recall1 = Koha::Recall->new({
779
        borrowernumber => $patron2->borrowernumber,
780
        recalldate => Koha::DateUtils::dt_from_string,
781
        biblionumber => $biblio->biblionumber,
782
        branchcode => $branchcode,
783
        status => 'R',
784
        itemnumber => $item1->itemnumber,
785
        expirationdate => undef,
786
        item_level_recall => 1
787
    })->store;
788
    $recall2 = Koha::Recall->new({
789
        borrowernumber => $patron1->borrowernumber,
790
        recalldate => Koha::DateUtils::dt_from_string,
791
        biblionumber => $biblio->biblionumber,
792
        branchcode => $branchcode,
793
        status => 'R',
794
        itemnumber => undef,
795
        expirationdate => undef,
796
        item_level_recall => 0
797
    })->store;
798
    $recall2->set_waiting({ item => $item1 });
799
800
    # return a waiting recall
801
    my $check_recall = $item1->check_recalls;
802
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
803
804
    $recall2->revert_waiting;
805
806
    # return recall based on recalldate
807
    $check_recall = $item1->check_recalls;
808
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
809
810
    $recall1->set_cancelled;
811
812
    # return a biblio-level recall
813
    $check_recall = $item1->check_recalls;
814
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
815
816
    $recall2->set_cancelled;
817
};
(-)a/t/db_dependent/Koha/Patron.t (+56 lines)
Lines 365-367 subtest 'is_superlibrarian() tests' => sub { Link Here
365
365
366
    $schema->storage->txn_rollback;
366
    $schema->storage->txn_rollback;
367
};
367
};
368
369
subtest 'recalls() tests' => sub {
370
371
    plan tests => 2;
372
373
    $schema->storage->txn_begin;
374
375
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
376
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
377
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
378
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
379
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
380
381
    Koha::Recall->new({
382
        biblionumber => $biblio1->biblionumber,
383
        borrowernumber => $patron->borrowernumber,
384
        itemnumber => $item1->itemnumber,
385
        branchcode => $patron->branchcode,
386
        recalldate => dt_from_string,
387
        status => 'R',
388
        item_level_recall => 1,
389
    })->store;
390
    Koha::Recall->new({
391
        biblionumber => $biblio2->biblionumber,
392
        borrowernumber => $patron->borrowernumber,
393
        itemnumber => $item2->itemnumber,
394
        branchcode => $patron->branchcode,
395
        recalldate => dt_from_string,
396
        status => 'R',
397
        item_level_recall => 1,
398
    })->store;
399
    Koha::Recall->new({
400
        biblionumber => $biblio1->biblionumber,
401
        borrowernumber => $patron->borrowernumber,
402
        itemnumber => undef,
403
        branchcode => $patron->branchcode,
404
        recalldate => dt_from_string,
405
        status => 'R',
406
        item_level_recall => 0,
407
    })->store;
408
    my $recall = Koha::Recall->new({
409
        biblionumber => $biblio1->biblionumber,
410
        borrowernumber => $patron->borrowernumber,
411
        itemnumber => undef,
412
        branchcode => $patron->branchcode,
413
        recalldate => dt_from_string,
414
        status => 'R',
415
        item_level_recall => 0,
416
    })->store;
417
    $recall->set_cancelled;
418
419
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
420
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
421
422
    $schema->storage->txn_rollback;
423
};
(-)a/t/db_dependent/Stats.t (-1 / +1 lines)
Lines 55-61 $return_error = $@; Link Here
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
56
56
57
# returns undef and croaks if mandatory params are missing
57
# returns undef and croaks if mandatory params are missing
58
my @allowed_circulation_types = qw (renew issue localuse return);
58
my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout recall);
59
my @allowed_accounts_types = qw (writeoff payment);
59
my @allowed_accounts_types = qw (writeoff payment);
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
(-)a/t/db_dependent/XSLT.t (-3 / +12 lines)
Lines 34-40 my $builder = t::lib::TestBuilder->new; Link Here
34
$schema->storage->txn_begin;
34
$schema->storage->txn_begin;
35
35
36
subtest 'buildKohaItemsNamespace status tests' => sub {
36
subtest 'buildKohaItemsNamespace status tests' => sub {
37
    plan tests => 13;
37
    plan tests => 14;
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
Lines 103-109 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
103
        }
103
        }
104
    });
104
    });
105
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
105
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
106
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit");
106
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (holds)");
107
    $hold->cancel;
107
108
108
    $builder->build({ source => "TmpHoldsqueue", value => {
109
    $builder->build({ source => "TmpHoldsqueue", value => {
109
        itemnumber => $item->itemnumber
110
        itemnumber => $item->itemnumber
Lines 112-117 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
112
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
113
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
113
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
114
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
114
115
116
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
117
        biblionumber    => $item->biblionumber,
118
        itemnumber      => $item->itemnumber,
119
        branchcode      => $item->holdingbranch,
120
        status          => 'R',
121
    }});
122
    $recall->set_waiting;
123
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
124
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
115
125
116
};
126
};
117
127
118
- 

Return to bug 19532