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
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2877
    # 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 353-358 sub CanBookBeReserved{ Link Here
353
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
353
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
354
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
354
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
355
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
355
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
356
         { status => recall }, if the borrower has already placed a recall on this item
356
357
357
=cut
358
=cut
358
359
Lines 386-391 sub CanItemBeReserved { Link Here
386
    return { status =>'itemAlreadyOnHold' }
387
    return { status =>'itemAlreadyOnHold' }
387
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
388
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
388
389
390
    # check if a recall exists on this item from this borrower
391
    return { status => 'recall' }
392
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
393
389
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
394
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
390
395
391
    my $querycount = q{
396
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1892-1897 sub searchResults { Link Here
1892
        my $can_place_holds       = 0;
1892
        my $can_place_holds       = 0;
1893
        my $item_onhold_count     = 0;
1893
        my $item_onhold_count     = 0;
1894
        my $notforloan_count      = 0;
1894
        my $notforloan_count      = 0;
1895
        my $item_recalled_count   = 0;
1895
        my $items_count           = scalar(@fields);
1896
        my $items_count           = scalar(@fields);
1896
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1897
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1897
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1898
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1986-1991 sub searchResults { Link Here
1986
                # is item on the reserve shelf?
1987
                # is item on the reserve shelf?
1987
                my $reservestatus = '';
1988
                my $reservestatus = '';
1988
1989
1990
                # is item a waiting recall?
1991
                my $recallstatus = '';
1992
1989
                unless ($item->{withdrawn}
1993
                unless ($item->{withdrawn}
1990
                        || $item->{itemlost}
1994
                        || $item->{itemlost}
1991
                        || $item->{damaged}
1995
                        || $item->{damaged}
Lines 2007-2012 sub searchResults { Link Here
2007
                    #
2011
                    #
2008
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2012
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2009
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2013
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2014
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
2010
                }
2015
                }
2011
2016
2012
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
2017
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 2015-2020 sub searchResults { Link Here
2015
                    || $item->{damaged}
2020
                    || $item->{damaged}
2016
                    || $item->{notforloan}
2021
                    || $item->{notforloan}
2017
                    || $reservestatus eq 'Waiting'
2022
                    || $reservestatus eq 'Waiting'
2023
                    || $recallstatus eq 'Waiting'
2018
                    || ($transfertwhen && $transfertwhen ne ''))
2024
                    || ($transfertwhen && $transfertwhen ne ''))
2019
                {
2025
                {
2020
                    $withdrawn_count++        if $item->{withdrawn};
2026
                    $withdrawn_count++        if $item->{withdrawn};
Lines 2022-2027 sub searchResults { Link Here
2022
                    $itemdamaged_count++     if $item->{damaged};
2028
                    $itemdamaged_count++     if $item->{damaged};
2023
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2029
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2024
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2030
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2031
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
2025
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2032
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2026
2033
2027
                    # can place a hold on a item if
2034
                    # can place a hold on a item if
Lines 2043-2048 sub searchResults { Link Here
2043
                        $other_items->{$key}->{$_} = $item->{$_};
2050
                        $other_items->{$key}->{$_} = $item->{$_};
2044
                    }
2051
                    }
2045
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2052
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2053
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
2046
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2054
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2047
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2055
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2048
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
2056
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2117-2122 sub searchResults { Link Here
2117
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2125
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2118
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2126
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2119
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2127
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2128
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2120
        $oldbiblio->{orderedcount}         = $ordered_count;
2129
        $oldbiblio->{orderedcount}         = $ordered_count;
2121
        $oldbiblio->{notforloancount}      = $notforloan_count;
2130
        $oldbiblio->{notforloancount}      = $notforloan_count;
2122
2131
(-)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 1251-1256 subtest "CanBookBeRenewed tests" => sub { Link Here
1251
            $item_3->itemcallnumber || '' ),
1251
            $item_3->itemcallnumber || '' ),
1252
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1252
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1253
    );
1253
    );
1254
1255
    # Recalls
1256
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1257
    Koha::CirculationRules->set_rules({
1258
        categorycode => undef,
1259
        branchcode => undef,
1260
        itemtype => undef,
1261
        rules => {
1262
            recalls_allowed => 10,
1263
            renewalsallowed => 5,
1264
        },
1265
    });
1266
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1267
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1268
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1269
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1270
1271
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1272
1273
    # item-level and this item: renewal not allowed
1274
    my $recall = Koha::Recall->new({
1275
        biblionumber => $recall_item1->biblionumber,
1276
        itemnumber => $recall_item1->itemnumber,
1277
        borrowernumber => $recall_borrower->borrowernumber,
1278
        branchcode => $recall_borrower->branchcode,
1279
        item_level_recall => 1,
1280
        status => 'R',
1281
    })->store;
1282
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1283
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1284
    $recall->set_cancelled;
1285
1286
    # biblio-level requested recall: renewal not allowed
1287
    $recall = Koha::Recall->new({
1288
        biblionumber => $recall_item1->biblionumber,
1289
        itemnumber => undef,
1290
        borrowernumber => $recall_borrower->borrowernumber,
1291
        branchcode => $recall_borrower->branchcode,
1292
        item_level_recall => 0,
1293
        status => 'R',
1294
    })->store;
1295
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1296
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1297
    $recall->set_cancelled;
1298
1299
    # item-level and not this item: renewal allowed
1300
    $recall = Koha::Recall->new({
1301
        biblionumber => $recall_item2->biblionumber,
1302
        itemnumber => $recall_item2->itemnumber,
1303
        borrowernumber => $recall_borrower->borrowernumber,
1304
        branchcode => $recall_borrower->branchcode,
1305
        item_level_recall => 1,
1306
        status => 'R',
1307
    })->store;
1308
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1309
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1310
    $recall->set_cancelled;
1311
1312
    # biblio-level waiting recall: renewal allowed
1313
    $recall = Koha::Recall->new({
1314
        biblionumber => $recall_item1->biblionumber,
1315
        itemnumber => undef,
1316
        borrowernumber => $recall_borrower->borrowernumber,
1317
        branchcode => $recall_borrower->branchcode,
1318
        item_level_recall => 0,
1319
        status => 'R',
1320
    })->store;
1321
    $recall->set_waiting({ item => $recall_item1 });
1322
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1323
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1324
    $recall->set_cancelled;
1254
};
1325
};
1255
1326
1256
subtest "GetUpcomingDueIssues" => sub {
1327
subtest "GetUpcomingDueIssues" => sub {
Lines 1731-1736 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1731
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1802
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1732
};
1803
};
1733
1804
1805
subtest 'AddIssue | recalls' => sub {
1806
    plan tests => 3;
1807
1808
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1809
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1810
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1811
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1812
    my $item = $builder->build_sample_item;
1813
    Koha::CirculationRules->set_rules({
1814
        branchcode => undef,
1815
        itemtype => undef,
1816
        categorycode => undef,
1817
        rules => {
1818
            recalls_allowed => 10,
1819
        },
1820
    });
1821
1822
    # checking out item that they have recalled
1823
    my $recall1 = Koha::Recall->new({
1824
        borrowernumber => $patron1->borrowernumber,
1825
        biblionumber => $item->biblionumber,
1826
        itemnumber => $item->itemnumber,
1827
        item_level_recall => 1,
1828
        branchcode => $patron1->branchcode,
1829
        status => 'R',
1830
    })->store;
1831
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1832
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1833
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1834
    AddReturn( $item->barcode, $item->homebranch );
1835
1836
    # this item is has a recall request. cancel recall
1837
    my $recall2 = Koha::Recall->new({
1838
        borrowernumber => $patron2->borrowernumber,
1839
        biblionumber => $item->biblionumber,
1840
        itemnumber => $item->itemnumber,
1841
        item_level_recall => 1,
1842
        branchcode => $patron2->branchcode,
1843
        status => 'R',
1844
    })->store;
1845
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1846
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1847
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1848
    AddReturn( $item->barcode, $item->homebranch );
1849
1850
    # this item is waiting to fulfill a recall. revert recall
1851
    my $recall3 = Koha::Recall->new({
1852
        borrowernumber => $patron2->borrowernumber,
1853
        biblionumber => $item->biblionumber,
1854
        itemnumber => $item->itemnumber,
1855
        item_level_recall => 1,
1856
        branchcode => $patron2->branchcode,
1857
        status => 'R',
1858
    })->store;
1859
    $recall3->set_waiting;
1860
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1861
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1862
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1863
    AddReturn( $item->barcode, $item->homebranch );
1864
};
1865
1866
1734
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1867
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1735
    plan tests => 8;
1868
    plan tests => 8;
1736
1869
Lines 3151-3156 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3151
    # TODO test with AllowNotForLoanOverride = 1
3284
    # TODO test with AllowNotForLoanOverride = 1
3152
};
3285
};
3153
3286
3287
subtest 'CanBookBeIssued | recalls' => sub {
3288
    plan tests => 2;
3289
3290
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3291
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3292
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3293
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3294
    my $item = $builder->build_sample_item;
3295
    Koha::CirculationRules->set_rules({
3296
        branchcode => undef,
3297
        itemtype => undef,
3298
        categorycode => undef,
3299
        rules => {
3300
            recalls_allowed => 10,
3301
        },
3302
    });
3303
3304
    # item-level recall
3305
    my $recall = Koha::Recall->new({
3306
        borrowernumber => $patron1->borrowernumber,
3307
        biblionumber => $item->biblionumber,
3308
        itemnumber => $item->itemnumber,
3309
        item_level_recall => 1,
3310
        branchcode => $patron1->branchcode,
3311
        status => 'R',
3312
    })->store;
3313
3314
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3315
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3316
3317
    $recall->set_cancelled;
3318
3319
    # biblio-level recall
3320
    $recall = Koha::Recall->new({
3321
        borrowernumber => $patron1->borrowernumber,
3322
        biblionumber => $item->biblionumber,
3323
        itemnumber => undef,
3324
        item_level_recall => 0,
3325
        branchcode => $patron1->branchcode,
3326
        status => 'R',
3327
    })->store;
3328
3329
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3330
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3331
3332
    $recall->set_cancelled;
3333
};
3334
3154
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3335
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3155
    plan tests => 1;
3336
    plan tests => 1;
3156
3337
Lines 3166-3171 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3166
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3347
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3167
};
3348
};
3168
3349
3350
subtest 'AddReturn | recalls' => sub {
3351
    plan tests => 3;
3352
3353
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3354
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3355
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3356
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3357
    my $item1 = $builder->build_sample_item;
3358
    Koha::CirculationRules->set_rules({
3359
        branchcode => undef,
3360
        itemtype => undef,
3361
        categorycode => undef,
3362
        rules => {
3363
            recalls_allowed => 10,
3364
        },
3365
    });
3366
3367
    # this item can fill a recall with pickup at this branch
3368
    AddIssue( $patron1->unblessed, $item1->barcode );
3369
    my $recall1 = Koha::Recall->new({
3370
        borrowernumber => $patron2->borrowernumber,
3371
        biblionumber => $item1->biblionumber,
3372
        itemnumber => $item1->itemnumber,
3373
        item_level_recall => 1,
3374
        branchcode => $item1->homebranch,
3375
        status => 'R',
3376
    })->store;
3377
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3378
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3379
    $recall1->set_cancelled;
3380
3381
    # this item can fill a recall but needs transfer
3382
    AddIssue( $patron1->unblessed, $item1->barcode );
3383
    $recall1 = Koha::Recall->new({
3384
        borrowernumber => $patron2->borrowernumber,
3385
        biblionumber => $item1->biblionumber,
3386
        itemnumber => $item1->itemnumber,
3387
        item_level_recall => 1,
3388
        branchcode => $patron2->branchcode,
3389
        status => 'R',
3390
    })->store;
3391
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3392
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3393
    $recall1->set_cancelled;
3394
3395
    # this item is already in transit, do not ask to transfer
3396
    AddIssue( $patron1->unblessed, $item1->barcode );
3397
    $recall1 = Koha::Recall->new({
3398
        borrowernumber => $patron2->borrowernumber,
3399
        biblionumber => $item1->biblionumber,
3400
        itemnumber => $item1->itemnumber,
3401
        item_level_recall => 1,
3402
        branchcode => $patron2->branchcode,
3403
        status => 'R',
3404
    })->store;
3405
    $recall1->start_transfer;
3406
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3407
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3408
    $recall1->set_cancelled;
3409
};
3169
3410
3170
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3411
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3171
3412
(-)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 / +28 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 => 67;
10
use Test::More tests => 68;
11
use MARC::Record;
11
use MARC::Record;
12
12
13
use C4::Biblio;
13
use C4::Biblio;
Lines 1348-1352 subtest 'non priority holds' => sub { Link Here
1348
    is( $err, 'on_reserve', 'Item is on hold' );
1348
    is( $err, 'on_reserve', 'Item is on hold' );
1349
1349
1350
    $schema->storage->txn_rollback;
1350
    $schema->storage->txn_rollback;
1351
};
1352
1353
subtest 'CanItemBeReserved / recall' => sub {
1354
    plan tests => 1;
1355
1356
    $schema->storage->txn_begin;
1357
1358
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1359
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1360
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1361
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1362
    my $item1   = $builder->build_sample_item(
1363
        {
1364
            biblionumber => $biblio1->biblionumber,
1365
            library      => $library1->branchcode
1366
        }
1367
    );
1368
    Koha::Recall->new({
1369
        borrowernumber => $patron1->borrowernumber,
1370
        biblionumber => $biblio1->biblionumber,
1371
        branchcode => $library1->branchcode,
1372
        itemnumber => $item1->itemnumber,
1373
        recalldate => '2020-05-04 10:10:10',
1374
        item_level_recall => 1,
1375
    })->store;
1376
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1351
1377
1378
    $schema->storage->txn_rollback;
1352
};
1379
};
(-)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 516-518 subtest 'Tests for itemtype' => sub { Link Here
516
516
517
    $schema->storage->txn_rollback;
517
    $schema->storage->txn_rollback;
518
};
518
};
519
520
subtest 'Recalls tests' => sub {
521
522
    plan tests => 20;
523
524
    $schema->storage->txn_begin;
525
526
    my $item1 = $builder->build_sample_item;
527
    my $biblio = $item1->biblio;
528
    my $branchcode = $item1->holdingbranch;
529
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
530
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
531
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
532
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
533
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
534
535
    my $recall1 = Koha::Recall->new({
536
        borrowernumber => $patron1->borrowernumber,
537
        recalldate => Koha::DateUtils::dt_from_string,
538
        biblionumber => $biblio->biblionumber,
539
        branchcode => $branchcode,
540
        status => 'R',
541
        itemnumber => $item1->itemnumber,
542
        expirationdate => undef,
543
        item_level_recall => 1
544
    })->store;
545
    my $recall2 = Koha::Recall->new({
546
        borrowernumber => $patron2->borrowernumber,
547
        recalldate => Koha::DateUtils::dt_from_string,
548
        biblionumber => $biblio->biblionumber,
549
        branchcode => $branchcode,
550
        status => 'R',
551
        itemnumber => $item1->itemnumber,
552
        expirationdate => undef,
553
        item_level_recall =>1
554
    })->store;
555
556
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
557
558
    $recall2->set_cancelled;
559
560
    t::lib::Mocks::mock_preference('UseRecalls', 0);
561
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
562
563
    t::lib::Mocks::mock_preference("UseRecalls", 1);
564
565
    $item1->update({ notforloan => 1 });
566
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
567
    $item1->update({ notforloan => 0, itemlost => 1 });
568
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
569
    $item1->update({ itemlost => 0, withdrawn => 1 });
570
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
571
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
572
573
    $item1->update({ withdrawn => 0 });
574
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
575
576
    Koha::CirculationRules->set_rules({
577
        branchcode => $branchcode,
578
        categorycode => $patron1->categorycode,
579
        itemtype => $item1->effective_itemtype,
580
        rules => {
581
            recalls_allowed => 0,
582
            recalls_per_record => 1,
583
            on_shelf_recalls => 'all',
584
        },
585
    });
586
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
587
588
    Koha::CirculationRules->set_rules({
589
        branchcode => $branchcode,
590
        categorycode => $patron1->categorycode,
591
        itemtype => $item1->effective_itemtype,
592
        rules => {
593
            recalls_allowed => 1,
594
            recalls_per_record => 1,
595
            on_shelf_recalls => 'all',
596
        },
597
    });
598
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
599
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
600
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
601
602
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
603
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
604
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
605
606
    $recall1->set_cancelled;
607
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
608
609
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
610
611
    Koha::CirculationRules->set_rules({
612
        branchcode => $branchcode,
613
        categorycode => $patron1->categorycode,
614
        itemtype => $item1->effective_itemtype,
615
        rules => {
616
            recalls_allowed => 1,
617
            recalls_per_record => 1,
618
            on_shelf_recalls => 'any',
619
        },
620
    });
621
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
622
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
623
624
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
625
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
626
627
    $recall1 = Koha::Recall->new({
628
        borrowernumber => $patron1->borrowernumber,
629
        recalldate => Koha::DateUtils::dt_from_string,
630
        biblionumber => $biblio->biblionumber,
631
        branchcode => $branchcode,
632
        status => 'R',
633
        itemnumber => undef,
634
        expirationdate => undef,
635
        item_level_recall => 0
636
    })->store;
637
638
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
639
640
    Koha::CirculationRules->set_rules({
641
        branchcode => undef,
642
        categorycode => undef,
643
        itemtype => $item1->effective_itemtype,
644
        rules => {
645
            recalls_allowed => 0,
646
            recalls_per_record => 1,
647
            on_shelf_recalls => 'any',
648
        },
649
    });
650
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
651
652
    Koha::CirculationRules->set_rules({
653
        branchcode => undef,
654
        categorycode => undef,
655
        itemtype => $item1->effective_itemtype,
656
        rules => {
657
            recalls_allowed => 1,
658
            recalls_per_record => 1,
659
            on_shelf_recalls => 'any',
660
        },
661
    });
662
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
663
664
    # check_recalls tests
665
666
    $recall1 = Koha::Recall->new({
667
        borrowernumber => $patron2->borrowernumber,
668
        recalldate => Koha::DateUtils::dt_from_string,
669
        biblionumber => $biblio->biblionumber,
670
        branchcode => $branchcode,
671
        status => 'R',
672
        itemnumber => $item1->itemnumber,
673
        expirationdate => undef,
674
        item_level_recall => 1
675
    })->store;
676
    $recall2 = Koha::Recall->new({
677
        borrowernumber => $patron1->borrowernumber,
678
        recalldate => Koha::DateUtils::dt_from_string,
679
        biblionumber => $biblio->biblionumber,
680
        branchcode => $branchcode,
681
        status => 'R',
682
        itemnumber => undef,
683
        expirationdate => undef,
684
        item_level_recall => 0
685
    })->store;
686
    $recall2->set_waiting({ item => $item1 });
687
688
    # return a waiting recall
689
    my $check_recall = $item1->check_recalls;
690
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
691
692
    $recall2->revert_waiting;
693
694
    # return recall based on recalldate
695
    $check_recall = $item1->check_recalls;
696
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
697
698
    $recall1->set_cancelled;
699
700
    # return a biblio-level recall
701
    $check_recall = $item1->check_recalls;
702
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
703
704
    $recall2->set_cancelled;
705
};
(-)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