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

(-)a/C4/Circulation.pm (-2 / +115 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 723-728 sticky due date is invalid or due date in the past Link Here
723
744
724
if the borrower borrows to much things
745
if the borrower borrows to much things
725
746
747
=head3 RECALLED
748
749
recalled by someone else
750
726
=cut
751
=cut
727
752
728
sub CanBookBeIssued {
753
sub CanBookBeIssued {
Lines 1074-1080 sub CanBookBeIssued { Link Here
1074
        }
1099
        }
1075
    }
1100
    }
1076
1101
1077
    unless ( $ignore_reserves ) {
1102
    my $recall;
1103
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1104
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1105
    # Don't look at recalls that are in transit
1106
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1107
        my @recalls = $biblio->recalls;
1108
1109
        foreach my $r ( @recalls ) {
1110
            if ( $r->itemnumber == $item_object->itemnumber and
1111
                $r->borrowernumber == $patron->borrowernumber and
1112
                $r->waiting ) {
1113
                # this item is already waiting for this borrower and the recall can be fulfilled
1114
                last;
1115
            }
1116
            elsif ( $r->itemnumber == $item_object->itemnumber and
1117
                $r->in_transit ) {
1118
                # recalled item is in transit
1119
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1120
            }
1121
            elsif ( $r->item_level_recall and
1122
                $r->itemnumber == $item_object->itemnumber and
1123
                $r->borrowernumber != $patron->borrowernumber and
1124
                !$r->in_transit ) {
1125
                # this specific item has been recalled by a different patron
1126
                $needsconfirmation{RECALLED} = $r;
1127
                $recall = $r;
1128
                last;
1129
            }
1130
            elsif ( !$r->item_level_recall and
1131
                $r->borrowernumber != $patron->borrowernumber and
1132
                !$r->in_transit ) {
1133
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1134
                $needsconfirmation{RECALLED} = $r;
1135
                $recall = $r;
1136
                last;
1137
            }
1138
        }
1139
    }
1140
1141
    unless ( $ignore_reserves and defined $recall ) {
1078
        # See if the item is on reserve.
1142
        # See if the item is on reserve.
1079
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1143
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1080
        if ($restype) {
1144
        if ($restype) {
Lines 1353-1358 AddIssue does the following things : Link Here
1353
          * RESERVE PLACED ?
1417
          * RESERVE PLACED ?
1354
              - fill reserve if reserve to this patron
1418
              - fill reserve if reserve to this patron
1355
              - cancel reserve or not, otherwise
1419
              - cancel reserve or not, otherwise
1420
          * RECALL PLACED ?
1421
              - fill recall if recall to this patron
1422
              - cancel recall or not
1423
              - revert recall's waiting status or not
1356
          * TRANSFERT PENDING ?
1424
          * TRANSFERT PENDING ?
1357
              - complete the transfert
1425
              - complete the transfert
1358
          * ISSUE THE BOOK
1426
          * ISSUE THE BOOK
Lines 1367-1372 sub AddIssue { Link Here
1367
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1435
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1368
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1436
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1369
    my $auto_renew = $params && $params->{auto_renew};
1437
    my $auto_renew = $params && $params->{auto_renew};
1438
    my $cancel_recall = $params && $params->{cancel_recall};
1439
    my $recall_id = $params && $params->{recall_id};
1370
    my $dbh          = C4::Context->dbh;
1440
    my $dbh          = C4::Context->dbh;
1371
    my $barcodecheck = CheckValidBarcode($barcode);
1441
    my $barcodecheck = CheckValidBarcode($barcode);
1372
1442
Lines 1438-1443 sub AddIssue { Link Here
1438
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1508
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1439
            }
1509
            }
1440
1510
1511
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1512
1441
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1513
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1442
1514
1443
            # Starting process for transfer job (checking transfert and validate it if we have one)
1515
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1845-1850 Value 1 if return is successful. Link Here
1845
1917
1846
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1918
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1847
1919
1920
=item C<RecallFound>
1921
1922
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
1923
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
1924
branchcode.
1925
1926
=item C<TransferredRecall>
1927
1928
This item has been transferred to this branch to fill a recall. The recall object is returned.
1929
1848
=back
1930
=back
1849
1931
1850
C<$iteminformation> is a reference-to-hash, giving information about the
1932
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2072-2077 sub AddReturn { Link Here
2072
        }
2154
        }
2073
    }
2155
    }
2074
2156
2157
    # find recalls...
2158
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2159
    my $recall = undef;
2160
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2161
    if ( defined $recall ) {
2162
        $messages->{RecallFound} = $recall;
2163
        if ( $recall->branchcode ne $branch ) {
2164
            $messages->{RecallNeedsTransfer} = $branch;
2165
        }
2166
    }
2167
2075
    # find reserves.....
2168
    # find reserves.....
2076
    # launch the Checkreserves routine to find any holds
2169
    # launch the Checkreserves routine to find any holds
2077
    my ($resfound, $resrec);
2170
    my ($resfound, $resrec);
Lines 2130-2137 sub AddReturn { Link Here
2130
        $request->status('RET') if $request;
2223
        $request->status('RET') if $request;
2131
    }
2224
    }
2132
2225
2226
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2227
    if ( $transfer_recall and
2228
         $transfer_recall->branchcode eq $branch and
2229
         C4::Context->preference('UseRecalls') ) {
2230
        $messages->{TransferredRecall} = $transfer_recall;
2231
    }
2232
2133
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2233
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2134
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
2234
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch)
2235
 and not $messages->{'WrongTransfer'} and not $messages->{TransferredRecall} and not $messages->{RecallNeedsTransfer} ){
2135
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2236
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2136
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2237
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2137
            (C4::Context->preference("UseBranchTransferLimits") and
2238
            (C4::Context->preference("UseBranchTransferLimits") and
Lines 2759-2764 sub CanBookBeRenewed { Link Here
2759
        }
2860
        }
2760
    }
2861
    }
2761
2862
2863
    my $recall = undef;
2864
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2865
    if ( defined $recall ) {
2866
        if ( $recall->item_level_recall ) {
2867
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2868
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2869
        } else {
2870
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2871
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2872
        }
2873
    }
2874
2762
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2875
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2763
2876
2764
    # This item can fill one or more unfilled reserve, can those unfilled reserves
2877
    # This item can fill one or more unfilled reserve, can those unfilled reserves
(-)a/C4/Reserves.pm (+5 lines)
Lines 351-356 sub CanBookBeReserved{ Link Here
351
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
351
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
352
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
352
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
353
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
353
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
354
         { status => recall }, if the borrower has already placed a recall on this item
354
355
355
=cut
356
=cut
356
357
Lines 384-389 sub CanItemBeReserved { Link Here
384
    return { status =>'itemAlreadyOnHold' }
385
    return { status =>'itemAlreadyOnHold' }
385
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
386
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
386
387
388
    # check if a recall exists on this item from this borrower
389
    return { status => 'recall' }
390
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
391
387
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
392
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
388
393
389
    my $querycount = q{
394
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1890-1895 sub searchResults { Link Here
1890
        my $can_place_holds       = 0;
1890
        my $can_place_holds       = 0;
1891
        my $item_onhold_count     = 0;
1891
        my $item_onhold_count     = 0;
1892
        my $notforloan_count      = 0;
1892
        my $notforloan_count      = 0;
1893
        my $item_recalled_count   = 0;
1893
        my $items_count           = scalar(@fields);
1894
        my $items_count           = scalar(@fields);
1894
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1895
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1895
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1896
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1984-1989 sub searchResults { Link Here
1984
                # is item on the reserve shelf?
1985
                # is item on the reserve shelf?
1985
                my $reservestatus = '';
1986
                my $reservestatus = '';
1986
1987
1988
                # is item a waiting recall?
1989
                my $recallstatus = '';
1990
1987
                unless ($item->{withdrawn}
1991
                unless ($item->{withdrawn}
1988
                        || $item->{itemlost}
1992
                        || $item->{itemlost}
1989
                        || $item->{damaged}
1993
                        || $item->{damaged}
Lines 2005-2010 sub searchResults { Link Here
2005
                    #
2009
                    #
2006
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2010
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2007
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2011
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2012
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
2008
                }
2013
                }
2009
2014
2010
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
2015
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 2013-2018 sub searchResults { Link Here
2013
                    || $item->{damaged}
2018
                    || $item->{damaged}
2014
                    || $item->{notforloan}
2019
                    || $item->{notforloan}
2015
                    || $reservestatus eq 'Waiting'
2020
                    || $reservestatus eq 'Waiting'
2021
                    || $recallstatus eq 'Waiting'
2016
                    || ($transfertwhen && $transfertwhen ne ''))
2022
                    || ($transfertwhen && $transfertwhen ne ''))
2017
                {
2023
                {
2018
                    $withdrawn_count++        if $item->{withdrawn};
2024
                    $withdrawn_count++        if $item->{withdrawn};
Lines 2020-2025 sub searchResults { Link Here
2020
                    $itemdamaged_count++     if $item->{damaged};
2026
                    $itemdamaged_count++     if $item->{damaged};
2021
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2027
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2022
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2028
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2029
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
2023
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2030
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2024
2031
2025
                    # can place a hold on a item if
2032
                    # can place a hold on a item if
Lines 2041-2046 sub searchResults { Link Here
2041
                        $other_items->{$key}->{$_} = $item->{$_};
2048
                        $other_items->{$key}->{$_} = $item->{$_};
2042
                    }
2049
                    }
2043
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2050
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2051
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
2044
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2052
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2045
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2053
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2046
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
2054
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2115-2120 sub searchResults { Link Here
2115
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2123
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2116
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2124
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2117
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2125
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2126
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2118
        $oldbiblio->{orderedcount}         = $ordered_count;
2127
        $oldbiblio->{orderedcount}         = $ordered_count;
2119
        $oldbiblio->{notforloancount}      = $notforloan_count;
2128
        $oldbiblio->{notforloancount}      = $notforloan_count;
2120
2129
(-)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 816-821 sub to_api_mapping { Link Here
816
    };
816
    };
817
}
817
}
818
818
819
=head3 recalls
820
821
    my @recalls = $biblio->recalls;
822
823
Return all active recalls attached to this biblio, sorted by oldest first
824
825
=cut
826
827
sub recalls {
828
    my ( $self ) = @_;
829
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
830
    return @recalls_rs;
831
}
832
833
=head3 can_be_recalled
834
835
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
836
837
Does biblio-level checks and returns the items attached to this biblio that are available for recall
838
839
=cut
840
841
sub can_be_recalled {
842
    my ( $self, $params ) = @_;
843
844
    return 0 if !( C4::Context->preference('UseRecalls') );
845
846
    my $patron = $params->{patron};
847
848
    my $branchcode = C4::Context->userenv->{'branch'};
849
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
850
        $branchcode = $patron->branchcode;
851
    }
852
853
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
854
855
    # if there are no available items at all, no recall can be placed
856
    return 0 if ( scalar @all_items == 0 );
857
858
    my @itemtypes;
859
    my @itemnumbers;
860
    my @items;
861
    foreach my $item ( @all_items ) {
862
        if ( $item->can_be_recalled({ patron => $patron }) ) {
863
            push( @itemtypes, $item->effective_itemtype );
864
            push( @itemnumbers, $item->itemnumber );
865
            push( @items, $item );
866
        }
867
    }
868
869
    # if there are no recallable items, no recall can be placed
870
    return 0 if ( scalar @items == 0 );
871
872
    # Check the circulation rule for each relevant itemtype for this biblio
873
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
874
    foreach my $itemtype ( @itemtypes ) {
875
        my $rule = Koha::CirculationRules->get_effective_rules({
876
            branchcode => $branchcode,
877
            categorycode => $patron ? $patron->categorycode : undef,
878
            itemtype => $itemtype,
879
            rules => [
880
                'recalls_allowed',
881
                'recalls_per_record',
882
                'on_shelf_recalls',
883
            ],
884
        });
885
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
886
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
887
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
888
    }
889
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
890
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
891
    my %on_shelf_recalls_count = ();
892
    foreach my $count ( @on_shelf_recalls ) {
893
        $on_shelf_recalls_count{$count}++;
894
    }
895
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
896
897
    # check recalls allowed has been set and is not zero
898
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
899
900
    if ( $patron ) {
901
        # check borrower has not reached open recalls allowed limit
902
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
903
904
        # check borrower has not reached open recalls allowed per record limit
905
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
906
907
        # check if any of the items under this biblio are already checked out by this borrower
908
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
909
    }
910
911
    # check item availability
912
    my $checked_out_count = 0;
913
    foreach (@items) {
914
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
915
    }
916
917
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
918
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
919
920
    # can't recall if no items have been checked out
921
    return 0 if ( $checked_out_count == 0 );
922
923
    # can recall
924
    return @items;
925
}
926
819
=head2 Internal methods
927
=head2 Internal methods
820
928
821
=head3 type
929
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 990-995 sub _after_item_action_hooks { Link Here
990
    );
990
    );
991
}
991
}
992
992
993
=head3 recall
994
995
    my $recall = $item->recall;
996
997
Return the relevant recall for this item
998
999
=cut
1000
1001
sub recall {
1002
    my ( $self ) = @_;
1003
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1004
    foreach my $recall (@recalls) {
1005
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1006
            return $recall;
1007
        }
1008
    }
1009
    # no item-level recall to return, so return earliest biblio-level
1010
    # FIXME: eventually this will be based on priority
1011
    return $recalls[0];
1012
}
1013
1014
=head3 can_be_recalled
1015
1016
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1017
1018
Does item-level checks and returns if items can be recalled by this borrower
1019
1020
=cut
1021
1022
sub can_be_recalled {
1023
    my ( $self, $params ) = @_;
1024
1025
    return 0 if !( C4::Context->preference('UseRecalls') );
1026
1027
    # check if this item is not for loan, withdrawn or lost
1028
    return 0 if ( $self->notforloan != 0 );
1029
    return 0 if ( $self->itemlost != 0 );
1030
    return 0 if ( $self->withdrawn != 0 );
1031
1032
    # check if this item is not checked out - if not checked out, can't be recalled
1033
    return 0 if ( !defined( $self->checkout ) );
1034
1035
    my $patron = $params->{patron};
1036
1037
    my $branchcode = C4::Context->userenv->{'branch'};
1038
    if ( $patron ) {
1039
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1040
    }
1041
1042
    # Check the circulation rule for each relevant itemtype for this item
1043
    my $rule = Koha::CirculationRules->get_effective_rules({
1044
        branchcode => $branchcode,
1045
        categorycode => $patron ? $patron->categorycode : undef,
1046
        itemtype => $self->effective_itemtype,
1047
        rules => [
1048
            'recalls_allowed',
1049
            'recalls_per_record',
1050
            'on_shelf_recalls',
1051
        ],
1052
    });
1053
1054
    # check recalls allowed has been set and is not zero
1055
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1056
1057
    if ( $patron ) {
1058
        # check borrower has not reached open recalls allowed limit
1059
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1060
1061
        # check borrower has not reach open recalls allowed per record limit
1062
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1063
1064
        # check if this patron has already recalled this item
1065
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1066
1067
        # check if this patron has already checked out this item
1068
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1069
1070
        # check if this patron has already reserved this item
1071
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1072
    }
1073
1074
    # check item availability
1075
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1076
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1077
1078
    # if there are no available items at all, no recall can be placed
1079
    return 0 if ( scalar @items == 0 );
1080
1081
    my $checked_out_count = 0;
1082
    foreach (@items) {
1083
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1084
    }
1085
1086
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1087
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1088
1089
    # can't recall if no items have been checked out
1090
    return 0 if ( $checked_out_count == 0 );
1091
1092
    # can recall
1093
    return 1;
1094
}
1095
1096
=head3 can_be_waiting_recall
1097
1098
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1099
1100
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1101
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1102
1103
=cut
1104
1105
sub can_be_waiting_recall {
1106
    my ( $self ) = @_;
1107
1108
    return 0 if !( C4::Context->preference('UseRecalls') );
1109
1110
    # check if this item is not for loan, withdrawn or lost
1111
    return 0 if ( $self->notforloan != 0 );
1112
    return 0 if ( $self->itemlost != 0 );
1113
    return 0 if ( $self->withdrawn != 0 );
1114
1115
    my $branchcode = $self->holdingbranch;
1116
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1117
        $branchcode = C4::Context->userenv->{'branch'};
1118
    } else {
1119
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1120
    }
1121
1122
    # Check the circulation rule for each relevant itemtype for this item
1123
    my $rule = Koha::CirculationRules->get_effective_rules({
1124
        branchcode => $branchcode,
1125
        categorycode => undef,
1126
        itemtype => $self->effective_itemtype,
1127
        rules => [
1128
            'recalls_allowed',
1129
        ],
1130
    });
1131
1132
    # check recalls allowed has been set and is not zero
1133
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1134
1135
    # can recall
1136
    return 1;
1137
}
1138
1139
=head3 check_recalls
1140
1141
    my $recall = $item->check_recalls;
1142
1143
Get the most relevant recall for this item.
1144
1145
=cut
1146
1147
sub check_recalls {
1148
    my ( $self ) = @_;
1149
1150
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1151
1152
    my $recall;
1153
    # iterate through relevant recalls to find the best one.
1154
    # if we come across a waiting recall, use this one.
1155
    # 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.
1156
    foreach my $r ( @recalls ) {
1157
        if ( $r->waiting ) {
1158
            $recall = $r;
1159
            last;
1160
        }
1161
    }
1162
    unless ( defined $recall ) {
1163
        $recall = $recalls[0];
1164
    }
1165
1166
    return $recall;
1167
}
1168
993
=head3 _type
1169
=head3 _type
994
1170
995
=cut
1171
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1712-1717 sub to_api_mapping { Link Here
1712
    };
1712
    };
1713
}
1713
}
1714
1714
1715
=head3 recalls
1716
1717
    my $recalls = $patron->recalls;
1718
1719
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1720
1721
Return the patron's active recalls - total, or on a specific biblio
1722
1723
=cut
1724
1725
sub recalls {
1726
    my ( $self, $params ) = @_;
1727
1728
    my $biblionumber = $params->{biblionumber};
1729
1730
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1731
1732
    if ( $biblionumber ) {
1733
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1734
    }
1735
1736
    return $recalls_rs;
1737
}
1738
1715
=head2 Internal methods
1739
=head2 Internal methods
1716
1740
1717
=head3 _type
1741
=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 / +243 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 => 48;
21
use Test::More tests => 51;
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 278-284 Koha::CirculationRules->set_rules( Link Here
278
278
279
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
279
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
280
subtest "CanBookBeRenewed tests" => sub {
280
subtest "CanBookBeRenewed tests" => sub {
281
    plan tests => 83;
281
    plan tests => 87;
282
282
283
    C4::Context->set_preference('ItemsDeniedRenewal','');
283
    C4::Context->set_preference('ItemsDeniedRenewal','');
284
    # Generate test biblio
284
    # Generate test biblio
Lines 1261-1266 subtest "CanBookBeRenewed tests" => sub { Link Here
1261
            $item_3->itemcallnumber || '' ),
1261
            $item_3->itemcallnumber || '' ),
1262
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1262
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1263
    );
1263
    );
1264
1265
    # Recalls
1266
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1267
    Koha::CirculationRules->set_rules({
1268
        categorycode => undef,
1269
        branchcode => undef,
1270
        itemtype => undef,
1271
        rules => {
1272
            recalls_allowed => 10,
1273
            renewalsallowed => 5,
1274
        },
1275
    });
1276
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1277
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1278
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1279
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1280
1281
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1282
1283
    # item-level and this item: renewal not allowed
1284
    my $recall = Koha::Recall->new({
1285
        biblionumber => $recall_item1->biblionumber,
1286
        itemnumber => $recall_item1->itemnumber,
1287
        borrowernumber => $recall_borrower->borrowernumber,
1288
        branchcode => $recall_borrower->branchcode,
1289
        item_level_recall => 1,
1290
        status => 'R',
1291
    })->store;
1292
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1293
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1294
    $recall->set_cancelled;
1295
1296
    # biblio-level requested recall: renewal not allowed
1297
    $recall = Koha::Recall->new({
1298
        biblionumber => $recall_item1->biblionumber,
1299
        itemnumber => undef,
1300
        borrowernumber => $recall_borrower->borrowernumber,
1301
        branchcode => $recall_borrower->branchcode,
1302
        item_level_recall => 0,
1303
        status => 'R',
1304
    })->store;
1305
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1306
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1307
    $recall->set_cancelled;
1308
1309
    # item-level and not this item: renewal allowed
1310
    $recall = Koha::Recall->new({
1311
        biblionumber => $recall_item2->biblionumber,
1312
        itemnumber => $recall_item2->itemnumber,
1313
        borrowernumber => $recall_borrower->borrowernumber,
1314
        branchcode => $recall_borrower->branchcode,
1315
        item_level_recall => 1,
1316
        status => 'R',
1317
    })->store;
1318
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1319
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1320
    $recall->set_cancelled;
1321
1322
    # biblio-level waiting recall: renewal allowed
1323
    $recall = Koha::Recall->new({
1324
        biblionumber => $recall_item1->biblionumber,
1325
        itemnumber => undef,
1326
        borrowernumber => $recall_borrower->borrowernumber,
1327
        branchcode => $recall_borrower->branchcode,
1328
        item_level_recall => 0,
1329
        status => 'R',
1330
    })->store;
1331
    $recall->set_waiting({ item => $recall_item1 });
1332
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1333
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1334
    $recall->set_cancelled;
1264
};
1335
};
1265
1336
1266
subtest "GetUpcomingDueIssues" => sub {
1337
subtest "GetUpcomingDueIssues" => sub {
Lines 1741-1746 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1741
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1812
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1742
};
1813
};
1743
1814
1815
subtest 'AddIssue | recalls' => sub {
1816
    plan tests => 3;
1817
1818
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1819
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1820
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1821
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1822
    my $item = $builder->build_sample_item;
1823
    Koha::CirculationRules->set_rules({
1824
        branchcode => undef,
1825
        itemtype => undef,
1826
        categorycode => undef,
1827
        rules => {
1828
            recalls_allowed => 10,
1829
        },
1830
    });
1831
1832
    # checking out item that they have recalled
1833
    my $recall1 = Koha::Recall->new({
1834
        borrowernumber => $patron1->borrowernumber,
1835
        biblionumber => $item->biblionumber,
1836
        itemnumber => $item->itemnumber,
1837
        item_level_recall => 1,
1838
        branchcode => $patron1->branchcode,
1839
        status => 'R',
1840
    })->store;
1841
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1842
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1843
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1844
    AddReturn( $item->barcode, $item->homebranch );
1845
1846
    # this item is has a recall request. cancel recall
1847
    my $recall2 = Koha::Recall->new({
1848
        borrowernumber => $patron2->borrowernumber,
1849
        biblionumber => $item->biblionumber,
1850
        itemnumber => $item->itemnumber,
1851
        item_level_recall => 1,
1852
        branchcode => $patron2->branchcode,
1853
        status => 'R',
1854
    })->store;
1855
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1856
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1857
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1858
    AddReturn( $item->barcode, $item->homebranch );
1859
1860
    # this item is waiting to fulfill a recall. revert recall
1861
    my $recall3 = Koha::Recall->new({
1862
        borrowernumber => $patron2->borrowernumber,
1863
        biblionumber => $item->biblionumber,
1864
        itemnumber => $item->itemnumber,
1865
        item_level_recall => 1,
1866
        branchcode => $patron2->branchcode,
1867
        status => 'R',
1868
    })->store;
1869
    $recall3->set_waiting;
1870
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1871
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1872
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1873
    AddReturn( $item->barcode, $item->homebranch );
1874
};
1875
1876
1744
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1877
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1745
    plan tests => 8;
1878
    plan tests => 8;
1746
1879
Lines 3161-3166 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3161
    # TODO test with AllowNotForLoanOverride = 1
3294
    # TODO test with AllowNotForLoanOverride = 1
3162
};
3295
};
3163
3296
3297
subtest 'CanBookBeIssued | recalls' => sub {
3298
    plan tests => 2;
3299
3300
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3301
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3302
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3303
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3304
    my $item = $builder->build_sample_item;
3305
    Koha::CirculationRules->set_rules({
3306
        branchcode => undef,
3307
        itemtype => undef,
3308
        categorycode => undef,
3309
        rules => {
3310
            recalls_allowed => 10,
3311
        },
3312
    });
3313
3314
    # item-level recall
3315
    my $recall = Koha::Recall->new({
3316
        borrowernumber => $patron1->borrowernumber,
3317
        biblionumber => $item->biblionumber,
3318
        itemnumber => $item->itemnumber,
3319
        item_level_recall => 1,
3320
        branchcode => $patron1->branchcode,
3321
        status => 'R',
3322
    })->store;
3323
3324
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3325
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3326
3327
    $recall->set_cancelled;
3328
3329
    # biblio-level recall
3330
    $recall = Koha::Recall->new({
3331
        borrowernumber => $patron1->borrowernumber,
3332
        biblionumber => $item->biblionumber,
3333
        itemnumber => undef,
3334
        item_level_recall => 0,
3335
        branchcode => $patron1->branchcode,
3336
        status => 'R',
3337
    })->store;
3338
3339
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3340
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3341
3342
    $recall->set_cancelled;
3343
};
3344
3164
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3345
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3165
    plan tests => 1;
3346
    plan tests => 1;
3166
3347
Lines 3176-3181 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3176
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3357
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3177
};
3358
};
3178
3359
3360
subtest 'AddReturn | recalls' => sub {
3361
    plan tests => 3;
3362
3363
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3364
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3365
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3366
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3367
    my $item1 = $builder->build_sample_item;
3368
    Koha::CirculationRules->set_rules({
3369
        branchcode => undef,
3370
        itemtype => undef,
3371
        categorycode => undef,
3372
        rules => {
3373
            recalls_allowed => 10,
3374
        },
3375
    });
3376
3377
    # this item can fill a recall with pickup at this branch
3378
    AddIssue( $patron1->unblessed, $item1->barcode );
3379
    my $recall1 = Koha::Recall->new({
3380
        borrowernumber => $patron2->borrowernumber,
3381
        biblionumber => $item1->biblionumber,
3382
        itemnumber => $item1->itemnumber,
3383
        item_level_recall => 1,
3384
        branchcode => $item1->homebranch,
3385
        status => 'R',
3386
    })->store;
3387
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3388
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3389
    $recall1->set_cancelled;
3390
3391
    # this item can fill a recall but needs transfer
3392
    AddIssue( $patron1->unblessed, $item1->barcode );
3393
    $recall1 = Koha::Recall->new({
3394
        borrowernumber => $patron2->borrowernumber,
3395
        biblionumber => $item1->biblionumber,
3396
        itemnumber => $item1->itemnumber,
3397
        item_level_recall => 1,
3398
        branchcode => $patron2->branchcode,
3399
        status => 'R',
3400
    })->store;
3401
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3402
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3403
    $recall1->set_cancelled;
3404
3405
    # this item is already in transit, do not ask to transfer
3406
    AddIssue( $patron1->unblessed, $item1->barcode );
3407
    $recall1 = Koha::Recall->new({
3408
        borrowernumber => $patron2->borrowernumber,
3409
        biblionumber => $item1->biblionumber,
3410
        itemnumber => $item1->itemnumber,
3411
        item_level_recall => 1,
3412
        branchcode => $patron2->branchcode,
3413
        status => 'R',
3414
    })->store;
3415
    $recall1->start_transfer;
3416
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3417
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3418
    $recall1->set_cancelled;
3419
};
3179
3420
3180
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3421
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3181
3422
(-)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 (-1 / +29 lines)
Lines 7-13 use t::lib::TestBuilder; Link Here
7
7
8
use C4::Context;
8
use C4::Context;
9
9
10
use Test::More tests => 66;
10
use Test::More tests => 67;
11
use MARC::Record;
11
use MARC::Record;
12
12
13
use C4::Biblio;
13
use C4::Biblio;
Lines 1251-1253 subtest 'CanItemBeReserved / pickup_not_in_hold_group' => sub { Link Here
1251
1251
1252
    $schema->storage->txn_rollback;
1252
    $schema->storage->txn_rollback;
1253
};
1253
};
1254
1255
subtest 'CanItemBeReserved / recall' => sub {
1256
    plan tests => 1;
1257
1258
    $schema->storage->txn_begin;
1259
1260
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1261
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1262
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1263
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1264
    my $item1   = $builder->build_sample_item(
1265
        {
1266
            biblionumber => $biblio1->biblionumber,
1267
            library      => $library1->branchcode
1268
        }
1269
    );
1270
    Koha::Recall->new({
1271
        borrowernumber => $patron1->borrowernumber,
1272
        biblionumber => $biblio1->biblionumber,
1273
        branchcode => $library1->branchcode,
1274
        itemnumber => $item1->itemnumber,
1275
        recalldate => '2020-05-04 10:10:10',
1276
        item_level_recall => 1,
1277
    })->store;
1278
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1279
1280
    $schema->storage->txn_rollback;
1281
};
(-)a/t/db_dependent/Koha/Biblio.t (-2 / +119 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 184-194 subtest 'pickup_locations' => sub { Link Here
184
184
185
    # Cleanup database
185
    # Cleanup database
186
    Koha::Holds->search->delete;
186
    Koha::Holds->search->delete;
187
    $dbh->do('DELETE FROM issues');
187
    Koha::Patrons->search->delete;
188
    Koha::Patrons->search->delete;
188
    Koha::Items->search->delete;
189
    Koha::Items->search->delete;
189
    Koha::Libraries->search->delete;
190
    Koha::Libraries->search->delete;
190
    Koha::CirculationRules->search->delete;
191
    Koha::CirculationRules->search->delete;
191
    $dbh->do('DELETE FROM issues');
192
    Koha::CirculationRules->set_rules(
192
    Koha::CirculationRules->set_rules(
193
        {
193
        {
194
            categorycode => undef,
194
            categorycode => undef,
Lines 549-551 subtest 'subscriptions() tests' => sub { Link Here
549
549
550
    $schema->storage->txn_rollback;
550
    $schema->storage->txn_rollback;
551
};
551
};
552
553
subtest 'Recalls tests' => sub {
554
555
    plan tests => 12;
556
557
    $schema->storage->txn_begin;
558
    my $item1 = $builder->build_sample_item;
559
    my $biblio = $item1->biblio;
560
    my $branchcode = $item1->holdingbranch;
561
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
562
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
563
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
564
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
565
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
566
567
    my $recall1 = Koha::Recall->new({
568
        borrowernumber => $patron1->borrowernumber,
569
        recalldate => Koha::DateUtils::dt_from_string,
570
        biblionumber => $biblio->biblionumber,
571
        branchcode => $branchcode,
572
        status => 'R',
573
        itemnumber => $item1->itemnumber,
574
        expirationdate => undef,
575
        item_level_recall => 1
576
    })->store;
577
    my $recall2 = Koha::Recall->new({
578
        borrowernumber => $patron2->borrowernumber,
579
        recalldate => Koha::DateUtils::dt_from_string,
580
        biblionumber => $biblio->biblionumber,
581
        branchcode => $branchcode,
582
        status => 'R',
583
        itemnumber => undef,
584
        expirationdate => undef,
585
        item_level_recall => 0
586
    })->store;
587
    my $recall3 = Koha::Recall->new({
588
        borrowernumber => $patron3->borrowernumber,
589
        recalldate => Koha::DateUtils::dt_from_string,
590
        biblionumber => $biblio->biblionumber,
591
        branchcode => $branchcode,
592
        status => 'R',
593
        itemnumber => $item1->itemnumber,
594
        expirationdate => undef,
595
        item_level_recall => 1
596
    })->store;
597
598
    my $recalls_count = scalar $biblio->recalls;
599
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
600
601
    $recall1->set_cancelled;
602
    $recall2->set_expired({ interface => 'COMMANDLINE' });
603
604
    $recalls_count = scalar $biblio->recalls;
605
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
606
607
    t::lib::Mocks::mock_preference('UseRecalls', 0);
608
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
609
610
    t::lib::Mocks::mock_preference("UseRecalls", 1);
611
    $item1->update({ notforloan => 1 });
612
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
613
614
    $item1->update({ notforloan => 0 });
615
    Koha::CirculationRules->set_rules({
616
        branchcode => $branchcode,
617
        categorycode => $patron1->categorycode,
618
        itemtype => $item1->effective_itemtype,
619
        rules => {
620
            recalls_allowed => 0,
621
            recalls_per_record => 1,
622
            on_shelf_recalls => 'all',
623
        },
624
    });
625
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
626
627
    Koha::CirculationRules->set_rules({
628
        branchcode => $branchcode,
629
        categorycode => $patron1->categorycode,
630
        itemtype => $item1->effective_itemtype,
631
        rules => {
632
            recalls_allowed => 1,
633
            recalls_per_record => 1,
634
            on_shelf_recalls => 'all',
635
        },
636
    });
637
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
638
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
639
640
    $recall1->set_cancelled;
641
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
642
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
643
644
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
645
646
    Koha::CirculationRules->set_rules({
647
        branchcode => $branchcode,
648
        categorycode => $patron1->categorycode,
649
        itemtype => $item1->effective_itemtype,
650
        rules => {
651
            recalls_allowed => 1,
652
            recalls_per_record => 1,
653
            on_shelf_recalls => 'any',
654
        },
655
    });
656
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
657
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
658
659
    $recall2->set_cancelled;
660
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
661
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
662
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
663
664
    $item1->update({ withdrawn => 1 });
665
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
666
667
    $schema->storage->txn_rollback;
668
};
(-)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 518-520 subtest 'Tests for itemtype' => sub { Link Here
518
518
519
    $schema->storage->txn_rollback;
519
    $schema->storage->txn_rollback;
520
};
520
};
521
522
subtest 'Recalls tests' => sub {
523
524
    plan tests => 20;
525
526
    $schema->storage->txn_begin;
527
528
    my $item1 = $builder->build_sample_item;
529
    my $biblio = $item1->biblio;
530
    my $branchcode = $item1->holdingbranch;
531
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
532
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
533
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
534
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
535
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
536
537
    my $recall1 = Koha::Recall->new({
538
        borrowernumber => $patron1->borrowernumber,
539
        recalldate => Koha::DateUtils::dt_from_string,
540
        biblionumber => $biblio->biblionumber,
541
        branchcode => $branchcode,
542
        status => 'R',
543
        itemnumber => $item1->itemnumber,
544
        expirationdate => undef,
545
        item_level_recall => 1
546
    })->store;
547
    my $recall2 = Koha::Recall->new({
548
        borrowernumber => $patron2->borrowernumber,
549
        recalldate => Koha::DateUtils::dt_from_string,
550
        biblionumber => $biblio->biblionumber,
551
        branchcode => $branchcode,
552
        status => 'R',
553
        itemnumber => $item1->itemnumber,
554
        expirationdate => undef,
555
        item_level_recall =>1
556
    })->store;
557
558
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
559
560
    $recall2->set_cancelled;
561
562
    t::lib::Mocks::mock_preference('UseRecalls', 0);
563
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
564
565
    t::lib::Mocks::mock_preference("UseRecalls", 1);
566
567
    $item1->update({ notforloan => 1 });
568
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
569
    $item1->update({ notforloan => 0, itemlost => 1 });
570
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
571
    $item1->update({ itemlost => 0, withdrawn => 1 });
572
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
573
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
574
575
    $item1->update({ withdrawn => 0 });
576
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
577
578
    Koha::CirculationRules->set_rules({
579
        branchcode => $branchcode,
580
        categorycode => $patron1->categorycode,
581
        itemtype => $item1->effective_itemtype,
582
        rules => {
583
            recalls_allowed => 0,
584
            recalls_per_record => 1,
585
            on_shelf_recalls => 'all',
586
        },
587
    });
588
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
589
590
    Koha::CirculationRules->set_rules({
591
        branchcode => $branchcode,
592
        categorycode => $patron1->categorycode,
593
        itemtype => $item1->effective_itemtype,
594
        rules => {
595
            recalls_allowed => 1,
596
            recalls_per_record => 1,
597
            on_shelf_recalls => 'all',
598
        },
599
    });
600
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
601
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
602
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
603
604
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
605
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
606
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
607
608
    $recall1->set_cancelled;
609
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
610
611
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
612
613
    Koha::CirculationRules->set_rules({
614
        branchcode => $branchcode,
615
        categorycode => $patron1->categorycode,
616
        itemtype => $item1->effective_itemtype,
617
        rules => {
618
            recalls_allowed => 1,
619
            recalls_per_record => 1,
620
            on_shelf_recalls => 'any',
621
        },
622
    });
623
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
624
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
625
626
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
627
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
628
629
    $recall1 = Koha::Recall->new({
630
        borrowernumber => $patron1->borrowernumber,
631
        recalldate => Koha::DateUtils::dt_from_string,
632
        biblionumber => $biblio->biblionumber,
633
        branchcode => $branchcode,
634
        status => 'R',
635
        itemnumber => undef,
636
        expirationdate => undef,
637
        item_level_recall => 0
638
    })->store;
639
640
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
641
642
    Koha::CirculationRules->set_rules({
643
        branchcode => undef,
644
        categorycode => undef,
645
        itemtype => $item1->effective_itemtype,
646
        rules => {
647
            recalls_allowed => 0,
648
            recalls_per_record => 1,
649
            on_shelf_recalls => 'any',
650
        },
651
    });
652
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
653
654
    Koha::CirculationRules->set_rules({
655
        branchcode => undef,
656
        categorycode => undef,
657
        itemtype => $item1->effective_itemtype,
658
        rules => {
659
            recalls_allowed => 1,
660
            recalls_per_record => 1,
661
            on_shelf_recalls => 'any',
662
        },
663
    });
664
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
665
666
    # check_recalls tests
667
668
    $recall1 = Koha::Recall->new({
669
        borrowernumber => $patron2->borrowernumber,
670
        recalldate => Koha::DateUtils::dt_from_string,
671
        biblionumber => $biblio->biblionumber,
672
        branchcode => $branchcode,
673
        status => 'R',
674
        itemnumber => $item1->itemnumber,
675
        expirationdate => undef,
676
        item_level_recall => 1
677
    })->store;
678
    $recall2 = Koha::Recall->new({
679
        borrowernumber => $patron1->borrowernumber,
680
        recalldate => Koha::DateUtils::dt_from_string,
681
        biblionumber => $biblio->biblionumber,
682
        branchcode => $branchcode,
683
        status => 'R',
684
        itemnumber => undef,
685
        expirationdate => undef,
686
        item_level_recall => 0
687
    })->store;
688
    $recall2->set_waiting({ item => $item1 });
689
690
    # return a waiting recall
691
    my $check_recall = $item1->check_recalls;
692
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
693
694
    $recall2->revert_waiting;
695
696
    # return recall based on recalldate
697
    $check_recall = $item1->check_recalls;
698
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
699
700
    $recall1->set_cancelled;
701
702
    # return a biblio-level recall
703
    $check_recall = $item1->check_recalls;
704
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
705
706
    $recall2->set_cancelled;
707
};
(-)a/t/db_dependent/Koha/Patron.t (-1 / +57 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 4;
22
use Test::More tests => 5;
23
use Test::Exception;
23
use Test::Exception;
24
24
25
use Koha::Database;
25
use Koha::Database;
Lines 211-213 subtest 'login_attempts tests' => sub { Link Here
211
211
212
    $schema->storage->txn_rollback;
212
    $schema->storage->txn_rollback;
213
};
213
};
214
215
subtest 'recalls() tests' => sub {
216
217
    plan tests => 2;
218
219
    $schema->storage->txn_begin;
220
221
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
222
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
223
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
224
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
225
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
226
227
    Koha::Recall->new({
228
        biblionumber => $biblio1->biblionumber,
229
        borrowernumber => $patron->borrowernumber,
230
        itemnumber => $item1->itemnumber,
231
        branchcode => $patron->branchcode,
232
        recalldate => dt_from_string,
233
        status => 'R',
234
        item_level_recall => 1,
235
    })->store;
236
    Koha::Recall->new({
237
        biblionumber => $biblio2->biblionumber,
238
        borrowernumber => $patron->borrowernumber,
239
        itemnumber => $item2->itemnumber,
240
        branchcode => $patron->branchcode,
241
        recalldate => dt_from_string,
242
        status => 'R',
243
        item_level_recall => 1,
244
    })->store;
245
    Koha::Recall->new({
246
        biblionumber => $biblio1->biblionumber,
247
        borrowernumber => $patron->borrowernumber,
248
        itemnumber => undef,
249
        branchcode => $patron->branchcode,
250
        recalldate => dt_from_string,
251
        status => 'R',
252
        item_level_recall => 0,
253
    })->store;
254
    my $recall = Koha::Recall->new({
255
        biblionumber => $biblio1->biblionumber,
256
        borrowernumber => $patron->borrowernumber,
257
        itemnumber => undef,
258
        branchcode => $patron->branchcode,
259
        recalldate => dt_from_string,
260
        status => 'R',
261
        item_level_recall => 0,
262
    })->store;
263
    $recall->set_cancelled;
264
265
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
266
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
267
268
    $schema->storage->txn_rollback;
269
};
(-)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