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

(-)a/C4/Circulation.pm (-2 / +120 lines)
Lines 297-302 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
297
297
298
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
298
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
299
299
300
=item C<RecallPlacedAtHoldingBranch>
301
302
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.
303
304
=item C<RecallFound>
305
306
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
307
300
=back
308
=back
301
309
302
=back
310
=back
Lines 370-375 sub transferbook { Link Here
370
        $dotransfer = 0 unless $ignoreRs;
378
        $dotransfer = 0 unless $ignoreRs;
371
    }
379
    }
372
380
381
    # find recall
382
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
383
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
384
        # do a transfer if the recall branch is different to the item holding branch
385
        if ( $recall->branchcode eq $fbr ) {
386
            $dotransfer = 0;
387
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
388
        } else {
389
            $dotransfer = 1;
390
            $messages->{'RecallFound'} = $recall;
391
        }
392
    }
393
373
    #actually do the transfer....
394
    #actually do the transfer....
374
    if ($dotransfer) {
395
    if ($dotransfer) {
375
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
396
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 720-725 sticky due date is invalid or due date in the past Link Here
720
741
721
if the borrower borrows to much things
742
if the borrower borrows to much things
722
743
744
=head3 RECALLED
745
746
recalled by someone else
747
723
=cut
748
=cut
724
749
725
sub CanBookBeIssued {
750
sub CanBookBeIssued {
Lines 1093-1099 sub CanBookBeIssued { Link Here
1093
        }
1118
        }
1094
    }
1119
    }
1095
1120
1096
    unless ( $ignore_reserves ) {
1121
    my $recall;
1122
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1123
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1124
    # Don't look at recalls that are in transit
1125
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1126
        my @recalls = $biblio->recalls;
1127
1128
        foreach my $r ( @recalls ) {
1129
            if ( $r->itemnumber and
1130
                $r->itemnumber == $item_object->itemnumber and
1131
                $r->borrowernumber == $patron->borrowernumber and
1132
                $r->waiting ) {
1133
                $messages{RECALLED} = $r->recall_id;
1134
                $recall = $r;
1135
                # this item is already waiting for this borrower and the recall can be fulfilled
1136
                last;
1137
            }
1138
            elsif ( $r->itemnumber and
1139
                $r->itemnumber == $item_object->itemnumber and
1140
                $r->in_transit ) {
1141
                # recalled item is in transit
1142
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1143
            }
1144
            elsif ( $r->item_level_recall and
1145
                $r->itemnumber == $item_object->itemnumber and
1146
                $r->borrowernumber != $patron->borrowernumber and
1147
                !$r->in_transit ) {
1148
                # this specific item has been recalled by a different patron
1149
                $needsconfirmation{RECALLED} = $r;
1150
                $recall = $r;
1151
                last;
1152
            }
1153
            elsif ( !$r->item_level_recall and
1154
                $r->borrowernumber != $patron->borrowernumber and
1155
                !$r->in_transit ) {
1156
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1157
                $needsconfirmation{RECALLED} = $r;
1158
                $recall = $r;
1159
                last;
1160
            }
1161
        }
1162
    }
1163
1164
    unless ( $ignore_reserves and defined $recall ) {
1097
        # See if the item is on reserve.
1165
        # See if the item is on reserve.
1098
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1166
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1099
        if ($restype) {
1167
        if ($restype) {
Lines 1408-1413 AddIssue does the following things : Link Here
1408
          * RESERVE PLACED ?
1476
          * RESERVE PLACED ?
1409
              - fill reserve if reserve to this patron
1477
              - fill reserve if reserve to this patron
1410
              - cancel reserve or not, otherwise
1478
              - cancel reserve or not, otherwise
1479
          * RECALL PLACED ?
1480
              - fill recall if recall to this patron
1481
              - cancel recall or not
1482
              - revert recall's waiting status or not
1411
          * TRANSFERT PENDING ?
1483
          * TRANSFERT PENDING ?
1412
              - complete the transfert
1484
              - complete the transfert
1413
          * ISSUE THE BOOK
1485
          * ISSUE THE BOOK
Lines 1422-1427 sub AddIssue { Link Here
1422
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1494
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1423
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1495
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1424
    my $auto_renew = $params && $params->{auto_renew};
1496
    my $auto_renew = $params && $params->{auto_renew};
1497
    my $cancel_recall = $params && $params->{cancel_recall};
1498
    my $recall_id = $params && $params->{recall_id};
1425
    my $dbh          = C4::Context->dbh;
1499
    my $dbh          = C4::Context->dbh;
1426
    my $barcodecheck = CheckValidBarcode($barcode);
1500
    my $barcodecheck = CheckValidBarcode($barcode);
1427
1501
Lines 1495-1500 sub AddIssue { Link Here
1495
                $item_object->discard_changes;
1569
                $item_object->discard_changes;
1496
            }
1570
            }
1497
1571
1572
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1573
1498
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1574
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1499
1575
1500
            # Starting process for transfer job (checking transfert and validate it if we have one)
1576
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1944-1949 Value 1 if return is successful. Link Here
1944
2020
1945
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2021
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1946
2022
2023
=item C<RecallFound>
2024
2025
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2026
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2027
branchcode.
2028
2029
=item C<TransferredRecall>
2030
2031
This item has been transferred to this branch to fill a recall. The recall object is returned.
2032
1947
=back
2033
=back
1948
2034
1949
C<$iteminformation> is a reference-to-hash, giving information about the
2035
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2212-2217 sub AddReturn { Link Here
2212
        }
2298
        }
2213
    }
2299
    }
2214
2300
2301
    # find recalls...
2302
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2303
    my $recall = undef;
2304
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2305
    if ( defined $recall ) {
2306
        $messages->{RecallFound} = $recall;
2307
        if ( $recall->branchcode ne $branch ) {
2308
            $messages->{RecallNeedsTransfer} = $branch;
2309
        }
2310
    }
2311
2215
    # find reserves.....
2312
    # find reserves.....
2216
    # launch the Checkreserves routine to find any holds
2313
    # launch the Checkreserves routine to find any holds
2217
    my ($resfound, $resrec);
2314
    my ($resfound, $resrec);
Lines 2270-2282 sub AddReturn { Link Here
2270
        $request->status('RET') if $request;
2367
        $request->status('RET') if $request;
2271
    }
2368
    }
2272
2369
2370
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2371
    if ( $transfer_recall and
2372
         $transfer_recall->branchcode eq $branch and
2373
         C4::Context->preference('UseRecalls') ) {
2374
        $messages->{TransferredRecall} = $transfer_recall;
2375
    }
2376
2273
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2377
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2274
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2378
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2275
        && ( $doreturn or $messages->{'NotIssued'} )
2379
        && ( $doreturn or $messages->{'NotIssued'} )
2276
        and !$resfound
2380
        and !$resfound
2277
        and ( $branch ne $returnbranch )
2381
        and ( $branch ne $returnbranch )
2278
        and not $messages->{'WrongTransfer'}
2382
        and not $messages->{'WrongTransfer'}
2279
        and not $messages->{'WasTransfered'} )
2383
        and not $messages->{'WasTransfered'}
2384
        and not $messages->{TransferredRecall}
2385
        and not $messages->{RecallNeedsTransfer} )
2280
    {
2386
    {
2281
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2387
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2282
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2388
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
Lines 2863-2868 sub CanBookBeRenewed { Link Here
2863
        }
2969
        }
2864
    }
2970
    }
2865
2971
2972
    my $recall = undef;
2973
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2974
    if ( defined $recall ) {
2975
        if ( $recall->item_level_recall ) {
2976
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2977
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2978
        } else {
2979
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2980
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2981
        }
2982
    }
2983
2866
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2984
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2867
2985
2868
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2986
    # 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 369-374 sub CanBookBeReserved{ Link Here
369
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
369
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
370
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
370
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
371
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
371
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
372
         { status => recall }, if the borrower has already placed a recall on this item
372
373
373
=cut
374
=cut
374
375
Lines 406-411 sub CanItemBeReserved { Link Here
406
        return { status =>'alreadypossession' };
407
        return { status =>'alreadypossession' };
407
    }
408
    }
408
409
410
    # check if a recall exists on this item from this borrower
411
    return { status => 'recall' }
412
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
413
409
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
414
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
410
415
411
    my $querycount = q{
416
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1813-1818 sub searchResults { Link Here
1813
        my $can_place_holds       = 0;
1813
        my $can_place_holds       = 0;
1814
        my $item_onhold_count     = 0;
1814
        my $item_onhold_count     = 0;
1815
        my $notforloan_count      = 0;
1815
        my $notforloan_count      = 0;
1816
        my $item_recalled_count   = 0;
1816
        my $items_count           = scalar(@fields);
1817
        my $items_count           = scalar(@fields);
1817
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1818
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1818
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1819
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1907-1912 sub searchResults { Link Here
1907
                # is item on the reserve shelf?
1908
                # is item on the reserve shelf?
1908
                my $reservestatus = '';
1909
                my $reservestatus = '';
1909
1910
1911
                # is item a waiting recall?
1912
                my $recallstatus = '';
1913
1910
                unless ($item->{withdrawn}
1914
                unless ($item->{withdrawn}
1911
                        || $item->{itemlost}
1915
                        || $item->{itemlost}
1912
                        || $item->{damaged}
1916
                        || $item->{damaged}
Lines 1928-1933 sub searchResults { Link Here
1928
                    #
1932
                    #
1929
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1933
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1930
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1934
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1935
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
1931
                }
1936
                }
1932
1937
1933
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
1938
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 1936-1941 sub searchResults { Link Here
1936
                    || $item->{damaged}
1941
                    || $item->{damaged}
1937
                    || $item->{notforloan}
1942
                    || $item->{notforloan}
1938
                    || $reservestatus eq 'Waiting'
1943
                    || $reservestatus eq 'Waiting'
1944
                    || $recallstatus eq 'Waiting'
1939
                    || ($transfertwhen && $transfertwhen ne ''))
1945
                    || ($transfertwhen && $transfertwhen ne ''))
1940
                {
1946
                {
1941
                    $withdrawn_count++        if $item->{withdrawn};
1947
                    $withdrawn_count++        if $item->{withdrawn};
Lines 1943-1948 sub searchResults { Link Here
1943
                    $itemdamaged_count++     if $item->{damaged};
1949
                    $itemdamaged_count++     if $item->{damaged};
1944
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1950
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1945
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1951
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1952
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
1946
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1953
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1947
1954
1948
                    # can place a hold on a item if
1955
                    # can place a hold on a item if
Lines 1964-1969 sub searchResults { Link Here
1964
                        $other_items->{$key}->{$_} = $item->{$_};
1971
                        $other_items->{$key}->{$_} = $item->{$_};
1965
                    }
1972
                    }
1966
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1973
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1974
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
1967
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1975
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1968
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1976
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1969
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
1977
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2038-2043 sub searchResults { Link Here
2038
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2046
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2039
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2047
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2040
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2048
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2049
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2041
        $oldbiblio->{orderedcount}         = $ordered_count;
2050
        $oldbiblio->{orderedcount}         = $ordered_count;
2042
        $oldbiblio->{notforloancount}      = $notforloan_count;
2051
        $oldbiblio->{notforloancount}      = $notforloan_count;
2043
2052
(-)a/C4/XSLT.pm (-1 / +7 lines)
Lines 333-339 sub buildKohaItemsNamespace { Link Here
333
        my $status;
333
        my $status;
334
        my $substatus = '';
334
        my $substatus = '';
335
335
336
        if ($item->has_pending_hold) {
336
        my $recalls = Koha::Recalls->search({ itemnumber => $item->itemnumber, status => 'W' });
337
338
        if ( $recalls->count ) {
339
            # recalls take priority over holds
340
            $status = 'Waiting';
341
        }
342
        elsif ( $item->has_pending_hold ) {
337
            $status = 'Pending hold';
343
            $status = 'Pending hold';
338
        }
344
        }
339
        elsif ( $item->holds->waiting->count ) {
345
        elsif ( $item->holds->waiting->count ) {
(-)a/Koha/Biblio.pm (+108 lines)
Lines 952-957 sub get_marc_host { Link Here
952
    }
952
    }
953
}
953
}
954
954
955
=head3 recalls
956
957
    my @recalls = $biblio->recalls;
958
959
Return all active recalls attached to this biblio, sorted by oldest first
960
961
=cut
962
963
sub recalls {
964
    my ( $self ) = @_;
965
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
966
    return @recalls_rs;
967
}
968
969
=head3 can_be_recalled
970
971
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
972
973
Does biblio-level checks and returns the items attached to this biblio that are available for recall
974
975
=cut
976
977
sub can_be_recalled {
978
    my ( $self, $params ) = @_;
979
980
    return 0 if !( C4::Context->preference('UseRecalls') );
981
982
    my $patron = $params->{patron};
983
984
    my $branchcode = C4::Context->userenv->{'branch'};
985
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
986
        $branchcode = $patron->branchcode;
987
    }
988
989
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
990
991
    # if there are no available items at all, no recall can be placed
992
    return 0 if ( scalar @all_items == 0 );
993
994
    my @itemtypes;
995
    my @itemnumbers;
996
    my @items;
997
    foreach my $item ( @all_items ) {
998
        if ( $item->can_be_recalled({ patron => $patron }) ) {
999
            push( @itemtypes, $item->effective_itemtype );
1000
            push( @itemnumbers, $item->itemnumber );
1001
            push( @items, $item );
1002
        }
1003
    }
1004
1005
    # if there are no recallable items, no recall can be placed
1006
    return 0 if ( scalar @items == 0 );
1007
1008
    # Check the circulation rule for each relevant itemtype for this biblio
1009
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
1010
    foreach my $itemtype ( @itemtypes ) {
1011
        my $rule = Koha::CirculationRules->get_effective_rules({
1012
            branchcode => $branchcode,
1013
            categorycode => $patron ? $patron->categorycode : undef,
1014
            itemtype => $itemtype,
1015
            rules => [
1016
                'recalls_allowed',
1017
                'recalls_per_record',
1018
                'on_shelf_recalls',
1019
            ],
1020
        });
1021
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
1022
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
1023
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
1024
    }
1025
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
1026
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
1027
    my %on_shelf_recalls_count = ();
1028
    foreach my $count ( @on_shelf_recalls ) {
1029
        $on_shelf_recalls_count{$count}++;
1030
    }
1031
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
1032
1033
    # check recalls allowed has been set and is not zero
1034
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
1035
1036
    if ( $patron ) {
1037
        # check borrower has not reached open recalls allowed limit
1038
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
1039
1040
        # check borrower has not reached open recalls allowed per record limit
1041
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
1042
1043
        # check if any of the items under this biblio are already checked out by this borrower
1044
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
1045
    }
1046
1047
    # check item availability
1048
    my $checked_out_count = 0;
1049
    foreach (@items) {
1050
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1051
    }
1052
1053
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1054
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
1055
1056
    # can't recall if no items have been checked out
1057
    return 0 if ( $checked_out_count == 0 );
1058
1059
    # can recall
1060
    return @items;
1061
}
1062
955
=head2 Internal methods
1063
=head2 Internal methods
956
1064
957
=head3 type
1065
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1196-1201 sub _after_item_action_hooks { Link Here
1196
    );
1196
    );
1197
}
1197
}
1198
1198
1199
=head3 recall
1200
1201
    my $recall = $item->recall;
1202
1203
Return the relevant recall for this item
1204
1205
=cut
1206
1207
sub recall {
1208
    my ( $self ) = @_;
1209
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1210
    foreach my $recall (@recalls) {
1211
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1212
            return $recall;
1213
        }
1214
    }
1215
    # no item-level recall to return, so return earliest biblio-level
1216
    # FIXME: eventually this will be based on priority
1217
    return $recalls[0];
1218
}
1219
1220
=head3 can_be_recalled
1221
1222
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1223
1224
Does item-level checks and returns if items can be recalled by this borrower
1225
1226
=cut
1227
1228
sub can_be_recalled {
1229
    my ( $self, $params ) = @_;
1230
1231
    return 0 if !( C4::Context->preference('UseRecalls') );
1232
1233
    # check if this item is not for loan, withdrawn or lost
1234
    return 0 if ( $self->notforloan != 0 );
1235
    return 0 if ( $self->itemlost != 0 );
1236
    return 0 if ( $self->withdrawn != 0 );
1237
1238
    # check if this item is not checked out - if not checked out, can't be recalled
1239
    return 0 if ( !defined( $self->checkout ) );
1240
1241
    my $patron = $params->{patron};
1242
1243
    my $branchcode = C4::Context->userenv->{'branch'};
1244
    if ( $patron ) {
1245
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1246
    }
1247
1248
    # Check the circulation rule for each relevant itemtype for this item
1249
    my $rule = Koha::CirculationRules->get_effective_rules({
1250
        branchcode => $branchcode,
1251
        categorycode => $patron ? $patron->categorycode : undef,
1252
        itemtype => $self->effective_itemtype,
1253
        rules => [
1254
            'recalls_allowed',
1255
            'recalls_per_record',
1256
            'on_shelf_recalls',
1257
        ],
1258
    });
1259
1260
    # check recalls allowed has been set and is not zero
1261
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1262
1263
    if ( $patron ) {
1264
        # check borrower has not reached open recalls allowed limit
1265
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1266
1267
        # check borrower has not reach open recalls allowed per record limit
1268
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1269
1270
        # check if this patron has already recalled this item
1271
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1272
1273
        # check if this patron has already checked out this item
1274
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1275
1276
        # check if this patron has already reserved this item
1277
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1278
    }
1279
1280
    # check item availability
1281
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1282
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1283
1284
    # if there are no available items at all, no recall can be placed
1285
    return 0 if ( scalar @items == 0 );
1286
1287
    my $checked_out_count = 0;
1288
    foreach (@items) {
1289
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1290
    }
1291
1292
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1293
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1294
1295
    # can't recall if no items have been checked out
1296
    return 0 if ( $checked_out_count == 0 );
1297
1298
    # can recall
1299
    return 1;
1300
}
1301
1302
=head3 can_be_waiting_recall
1303
1304
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1305
1306
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1307
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1308
1309
=cut
1310
1311
sub can_be_waiting_recall {
1312
    my ( $self ) = @_;
1313
1314
    return 0 if !( C4::Context->preference('UseRecalls') );
1315
1316
    # check if this item is not for loan, withdrawn or lost
1317
    return 0 if ( $self->notforloan != 0 );
1318
    return 0 if ( $self->itemlost != 0 );
1319
    return 0 if ( $self->withdrawn != 0 );
1320
1321
    my $branchcode = $self->holdingbranch;
1322
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1323
        $branchcode = C4::Context->userenv->{'branch'};
1324
    } else {
1325
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1326
    }
1327
1328
    # Check the circulation rule for each relevant itemtype for this item
1329
    my $rule = Koha::CirculationRules->get_effective_rules({
1330
        branchcode => $branchcode,
1331
        categorycode => undef,
1332
        itemtype => $self->effective_itemtype,
1333
        rules => [
1334
            'recalls_allowed',
1335
        ],
1336
    });
1337
1338
    # check recalls allowed has been set and is not zero
1339
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1340
1341
    # can recall
1342
    return 1;
1343
}
1344
1345
=head3 check_recalls
1346
1347
    my $recall = $item->check_recalls;
1348
1349
Get the most relevant recall for this item.
1350
1351
=cut
1352
1353
sub check_recalls {
1354
    my ( $self ) = @_;
1355
1356
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1357
1358
    my $recall;
1359
    # iterate through relevant recalls to find the best one.
1360
    # if we come across a waiting recall, use this one.
1361
    # 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.
1362
    foreach my $r ( @recalls ) {
1363
        if ( $r->waiting ) {
1364
            $recall = $r;
1365
            last;
1366
        }
1367
    }
1368
    unless ( defined $recall ) {
1369
        $recall = $recalls[0];
1370
    }
1371
1372
    return $recall;
1373
}
1374
1199
=head3 _type
1375
=head3 _type
1200
1376
1201
=cut
1377
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1918-1923 sub queue_notice { Link Here
1918
    return \%return;
1918
    return \%return;
1919
}
1919
}
1920
1920
1921
=head3 recalls
1922
1923
    my $recalls = $patron->recalls;
1924
1925
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1926
1927
Return the patron's active recalls - total, or on a specific biblio
1928
1929
=cut
1930
1931
sub recalls {
1932
    my ( $self, $params ) = @_;
1933
1934
    my $biblionumber = $params->{biblionumber};
1935
1936
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1937
1938
    if ( $biblionumber ) {
1939
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1940
    }
1941
1942
    return $recalls_rs;
1943
}
1944
1921
=head2 Internal methods
1945
=head2 Internal methods
1922
1946
1923
=head3 _type
1947
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 27-32 use Koha::Biblios; Link Here
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::ArticleRequest::Status;
29
use Koha::ArticleRequest::Status;
30
use Koha::Recalls;
30
31
31
sub HoldsCount {
32
sub HoldsCount {
32
    my ( $self, $biblionumber ) = @_;
33
    my ( $self, $biblionumber ) = @_;
Lines 63-66 sub CanArticleRequest { Link Here
63
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
}
65
}
65
66
67
sub RecallsCount {
68
    my ( $self, $biblionumber ) = @_;
69
70
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
71
72
    return $recalls->count;
73
}
74
66
1;
75
1;
(-)a/t/db_dependent/Circulation.t (-2 / +259 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
use utf8;
19
use utf8;
20
20
21
use Test::More tests => 55;
21
use Test::More tests => 58;
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 415-421 subtest "GetIssuingCharges tests" => sub { Link Here
415
415
416
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
416
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
417
subtest "CanBookBeRenewed tests" => sub {
417
subtest "CanBookBeRenewed tests" => sub {
418
    plan tests => 95;
418
    plan tests => 99;
419
419
420
    C4::Context->set_preference('ItemsDeniedRenewal','');
420
    C4::Context->set_preference('ItemsDeniedRenewal','');
421
    # Generate test biblio
421
    # Generate test biblio
Lines 1444-1449 subtest "CanBookBeRenewed tests" => sub { Link Here
1444
            $item_3->itemcallnumber || '' ),
1444
            $item_3->itemcallnumber || '' ),
1445
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1445
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1446
    );
1446
    );
1447
1448
    # Recalls
1449
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1450
    Koha::CirculationRules->set_rules({
1451
        categorycode => undef,
1452
        branchcode => undef,
1453
        itemtype => undef,
1454
        rules => {
1455
            recalls_allowed => 10,
1456
            renewalsallowed => 5,
1457
        },
1458
    });
1459
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1460
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1461
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1462
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1463
1464
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1465
1466
    # item-level and this item: renewal not allowed
1467
    my $recall = Koha::Recall->new({
1468
        biblionumber => $recall_item1->biblionumber,
1469
        itemnumber => $recall_item1->itemnumber,
1470
        borrowernumber => $recall_borrower->borrowernumber,
1471
        branchcode => $recall_borrower->branchcode,
1472
        item_level_recall => 1,
1473
        status => 'R',
1474
    })->store;
1475
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1476
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1477
    $recall->set_cancelled;
1478
1479
    # biblio-level requested recall: renewal not allowed
1480
    $recall = Koha::Recall->new({
1481
        biblionumber => $recall_item1->biblionumber,
1482
        itemnumber => undef,
1483
        borrowernumber => $recall_borrower->borrowernumber,
1484
        branchcode => $recall_borrower->branchcode,
1485
        item_level_recall => 0,
1486
        status => 'R',
1487
    })->store;
1488
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1489
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1490
    $recall->set_cancelled;
1491
1492
    # item-level and not this item: renewal allowed
1493
    $recall = Koha::Recall->new({
1494
        biblionumber => $recall_item2->biblionumber,
1495
        itemnumber => $recall_item2->itemnumber,
1496
        borrowernumber => $recall_borrower->borrowernumber,
1497
        branchcode => $recall_borrower->branchcode,
1498
        item_level_recall => 1,
1499
        status => 'R',
1500
    })->store;
1501
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1502
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1503
    $recall->set_cancelled;
1504
1505
    # biblio-level waiting recall: renewal allowed
1506
    $recall = Koha::Recall->new({
1507
        biblionumber => $recall_item1->biblionumber,
1508
        itemnumber => undef,
1509
        borrowernumber => $recall_borrower->borrowernumber,
1510
        branchcode => $recall_borrower->branchcode,
1511
        item_level_recall => 0,
1512
        status => 'R',
1513
    })->store;
1514
    $recall->set_waiting({ item => $recall_item1 });
1515
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1516
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1517
    $recall->set_cancelled;
1447
};
1518
};
1448
1519
1449
subtest "GetUpcomingDueIssues" => sub {
1520
subtest "GetUpcomingDueIssues" => sub {
Lines 1924-1929 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1924
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1995
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1925
};
1996
};
1926
1997
1998
subtest 'AddIssue | recalls' => sub {
1999
    plan tests => 3;
2000
2001
    t::lib::Mocks::mock_preference("UseRecalls", 1);
2002
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
2003
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
2004
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
2005
    my $item = $builder->build_sample_item;
2006
    Koha::CirculationRules->set_rules({
2007
        branchcode => undef,
2008
        itemtype => undef,
2009
        categorycode => undef,
2010
        rules => {
2011
            recalls_allowed => 10,
2012
        },
2013
    });
2014
2015
    # checking out item that they have recalled
2016
    my $recall1 = Koha::Recall->new({
2017
        borrowernumber => $patron1->borrowernumber,
2018
        biblionumber => $item->biblionumber,
2019
        itemnumber => $item->itemnumber,
2020
        item_level_recall => 1,
2021
        branchcode => $patron1->branchcode,
2022
        status => 'R',
2023
    })->store;
2024
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
2025
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
2026
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
2027
    AddReturn( $item->barcode, $item->homebranch );
2028
2029
    # this item is has a recall request. cancel recall
2030
    my $recall2 = Koha::Recall->new({
2031
        borrowernumber => $patron2->borrowernumber,
2032
        biblionumber => $item->biblionumber,
2033
        itemnumber => $item->itemnumber,
2034
        item_level_recall => 1,
2035
        branchcode => $patron2->branchcode,
2036
        status => 'R',
2037
    })->store;
2038
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
2039
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
2040
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
2041
    AddReturn( $item->barcode, $item->homebranch );
2042
2043
    # this item is waiting to fulfill a recall. revert recall
2044
    my $recall3 = Koha::Recall->new({
2045
        borrowernumber => $patron2->borrowernumber,
2046
        biblionumber => $item->biblionumber,
2047
        itemnumber => $item->itemnumber,
2048
        item_level_recall => 1,
2049
        branchcode => $patron2->branchcode,
2050
        status => 'R',
2051
    })->store;
2052
    $recall3->set_waiting;
2053
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
2054
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
2055
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
2056
    AddReturn( $item->barcode, $item->homebranch );
2057
};
2058
2059
1927
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
2060
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1928
    plan tests => 8;
2061
    plan tests => 8;
1929
2062
Lines 3845-3850 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3845
    # TODO test with AllowNotForLoanOverride = 1
3978
    # TODO test with AllowNotForLoanOverride = 1
3846
};
3979
};
3847
3980
3981
subtest 'CanBookBeIssued | recalls' => sub {
3982
    plan tests => 3;
3983
3984
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3985
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3986
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3987
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3988
    my $item = $builder->build_sample_item;
3989
    Koha::CirculationRules->set_rules({
3990
        branchcode => undef,
3991
        itemtype => undef,
3992
        categorycode => undef,
3993
        rules => {
3994
            recalls_allowed => 10,
3995
        },
3996
    });
3997
3998
    # item-level recall
3999
    my $recall = Koha::Recall->new({
4000
        borrowernumber => $patron1->borrowernumber,
4001
        biblionumber => $item->biblionumber,
4002
        itemnumber => $item->itemnumber,
4003
        item_level_recall => 1,
4004
        branchcode => $patron1->branchcode,
4005
        status => 'R',
4006
    })->store;
4007
4008
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4009
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
4010
4011
    $recall->set_cancelled;
4012
4013
    # biblio-level recall
4014
    $recall = Koha::Recall->new({
4015
        borrowernumber => $patron1->borrowernumber,
4016
        biblionumber => $item->biblionumber,
4017
        itemnumber => undef,
4018
        item_level_recall => 0,
4019
        branchcode => $patron1->branchcode,
4020
        status => 'R',
4021
    })->store;
4022
4023
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4024
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
4025
4026
    $recall->set_cancelled;
4027
4028
    # biblio-level recall
4029
    $recall = Koha::Recall->new({
4030
        borrowernumber => $patron1->borrowernumber,
4031
        biblionumber => $item->biblionumber,
4032
        itemnumber => undef,
4033
        item_level_recall => 0,
4034
        branchcode => $patron1->branchcode,
4035
        status => 'R',
4036
    })->store;
4037
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
4038
4039
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
4040
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
4041
4042
    $recall->set_cancelled;
4043
};
4044
3848
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
4045
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3849
    plan tests => 1;
4046
    plan tests => 1;
3850
4047
Lines 3860-3865 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3860
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
4057
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3861
};
4058
};
3862
4059
4060
subtest 'AddReturn | recalls' => sub {
4061
    plan tests => 3;
4062
4063
    t::lib::Mocks::mock_preference("UseRecalls", 1);
4064
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
4065
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
4066
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
4067
    my $item1 = $builder->build_sample_item;
4068
    Koha::CirculationRules->set_rules({
4069
        branchcode => undef,
4070
        itemtype => undef,
4071
        categorycode => undef,
4072
        rules => {
4073
            recalls_allowed => 10,
4074
        },
4075
    });
4076
4077
    # this item can fill a recall with pickup at this branch
4078
    AddIssue( $patron1->unblessed, $item1->barcode );
4079
    my $recall1 = Koha::Recall->new({
4080
        borrowernumber => $patron2->borrowernumber,
4081
        biblionumber => $item1->biblionumber,
4082
        itemnumber => $item1->itemnumber,
4083
        item_level_recall => 1,
4084
        branchcode => $item1->homebranch,
4085
        status => 'R',
4086
    })->store;
4087
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4088
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
4089
    $recall1->set_cancelled;
4090
4091
    # this item can fill a recall but needs transfer
4092
    AddIssue( $patron1->unblessed, $item1->barcode );
4093
    $recall1 = Koha::Recall->new({
4094
        borrowernumber => $patron2->borrowernumber,
4095
        biblionumber => $item1->biblionumber,
4096
        itemnumber => $item1->itemnumber,
4097
        item_level_recall => 1,
4098
        branchcode => $patron2->branchcode,
4099
        status => 'R',
4100
    })->store;
4101
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4102
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
4103
    $recall1->set_cancelled;
4104
4105
    # this item is already in transit, do not ask to transfer
4106
    AddIssue( $patron1->unblessed, $item1->barcode );
4107
    $recall1 = Koha::Recall->new({
4108
        borrowernumber => $patron2->borrowernumber,
4109
        biblionumber => $item1->biblionumber,
4110
        itemnumber => $item1->itemnumber,
4111
        item_level_recall => 1,
4112
        branchcode => $patron2->branchcode,
4113
        status => 'R',
4114
    })->store;
4115
    $recall1->start_transfer;
4116
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
4117
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
4118
    $recall1->set_cancelled;
4119
};
3863
4120
3864
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
4121
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3865
4122
(-)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 101-107 subtest 'field population tests' => sub { Link Here
101
#FIXME:'UseBranchTransferLimits tests missing
104
#FIXME:'UseBranchTransferLimits tests missing
102
105
103
subtest 'transfer already at destination' => sub {
106
subtest 'transfer already at destination' => sub {
104
    plan tests => 5;
107
    plan tests => 9;
105
108
106
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
109
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
107
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
110
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
Lines 151-156 subtest 'transfer already at destination' => sub { Link Here
151
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
154
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
152
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
155
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
153
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
156
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
157
158
    # recalls
159
    t::lib::Mocks::mock_preference('UseRecalls', 1);
160
    my $recall = Koha::Recall->new({
161
        biblionumber => $item->biblionumber,
162
        itemnumber => $item->itemnumber,
163
        item_level_recall => 1,
164
        borrowernumber => $patron->borrowernumber,
165
        branchcode => $library->branchcode,
166
        status => 'R',
167
    })->store;
168
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
169
    is( $dotransfer, 0, 'Do not transfer recalled item, it has already arrived' );
170
    is( $messages->{RecallPlacedAtHoldingBranch}, 1, "We found the recall");
171
172
    my $item2 = $builder->build_object({ class => 'Koha::Items' }); # this item will have a different holding branch to the pickup branch
173
    $recall = Koha::Recall->new({
174
        biblionumber => $item2->biblionumber,
175
        itemnumber => $item2->itemnumber,
176
        item_level_recall => 1,
177
        borrowernumber => $patron->borrowernumber,
178
        branchcode => $library->branchcode,
179
        status => 'R',
180
    })->store;
181
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
182
    is( $dotransfer, 1, 'Transfer of recalled item succeeded' );
183
    is( $messages->{RecallFound}->recall_id, $recall->recall_id, "We found the recall");
154
};
184
};
155
185
156
subtest 'transfer an issued item' => sub {
186
subtest 'transfer an issued item' => sub {
Lines 301-303 subtest 'transferbook test from branch' => sub { Link Here
301
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
331
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
302
332
303
};
333
};
334
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (+27 lines)
Lines 1539-1545 subtest 'non priority holds' => sub { Link Here
1539
    is( $err, 'on_reserve', 'Item is on hold' );
1539
    is( $err, 'on_reserve', 'Item is on hold' );
1540
1540
1541
    $schema->storage->txn_rollback;
1541
    $schema->storage->txn_rollback;
1542
};
1543
1544
subtest 'CanItemBeReserved / recall' => sub {
1545
    plan tests => 1;
1546
1547
    $schema->storage->txn_begin;
1548
1549
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1550
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1551
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1552
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1553
    my $item1   = $builder->build_sample_item(
1554
        {
1555
            biblionumber => $biblio1->biblionumber,
1556
            library      => $library1->branchcode
1557
        }
1558
    );
1559
    Koha::Recall->new({
1560
        borrowernumber => $patron1->borrowernumber,
1561
        biblionumber => $biblio1->biblionumber,
1562
        branchcode => $library1->branchcode,
1563
        itemnumber => $item1->itemnumber,
1564
        recalldate => '2020-05-04 10:10:10',
1565
        item_level_recall => 1,
1566
    })->store;
1567
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1542
1568
1569
    $schema->storage->txn_rollback;
1543
};
1570
};
1544
1571
1545
subtest 'CanItemBeReserved rule precedence tests' => sub {
1572
subtest 'CanItemBeReserved rule precedence tests' => sub {
(-)a/t/db_dependent/Koha/Biblio.t (-1 / +118 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 14;
20
use Test::More tests => 15;
21
21
22
use C4::Biblio qw( AddBiblio ModBiblio );
22
use C4::Biblio qw( AddBiblio ModBiblio );
23
use Koha::Database;
23
use Koha::Database;
Lines 637-639 subtest 'get_marc_notes() UNIMARC tests' => sub { Link Here
637
637
638
    $schema->storage->txn_rollback;
638
    $schema->storage->txn_rollback;
639
};
639
};
640
641
subtest 'Recalls tests' => sub {
642
643
    plan tests => 12;
644
645
    $schema->storage->txn_begin;
646
    my $item1 = $builder->build_sample_item;
647
    my $biblio = $item1->biblio;
648
    my $branchcode = $item1->holdingbranch;
649
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
650
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
651
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
652
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
653
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
654
655
    my $recall1 = Koha::Recall->new({
656
        borrowernumber => $patron1->borrowernumber,
657
        recalldate => Koha::DateUtils::dt_from_string,
658
        biblionumber => $biblio->biblionumber,
659
        branchcode => $branchcode,
660
        status => 'R',
661
        itemnumber => $item1->itemnumber,
662
        expirationdate => undef,
663
        item_level_recall => 1
664
    })->store;
665
    my $recall2 = Koha::Recall->new({
666
        borrowernumber => $patron2->borrowernumber,
667
        recalldate => Koha::DateUtils::dt_from_string,
668
        biblionumber => $biblio->biblionumber,
669
        branchcode => $branchcode,
670
        status => 'R',
671
        itemnumber => undef,
672
        expirationdate => undef,
673
        item_level_recall => 0
674
    })->store;
675
    my $recall3 = Koha::Recall->new({
676
        borrowernumber => $patron3->borrowernumber,
677
        recalldate => Koha::DateUtils::dt_from_string,
678
        biblionumber => $biblio->biblionumber,
679
        branchcode => $branchcode,
680
        status => 'R',
681
        itemnumber => $item1->itemnumber,
682
        expirationdate => undef,
683
        item_level_recall => 1
684
    })->store;
685
686
    my $recalls_count = scalar $biblio->recalls;
687
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
688
689
    $recall1->set_cancelled;
690
    $recall2->set_expired({ interface => 'COMMANDLINE' });
691
692
    $recalls_count = scalar $biblio->recalls;
693
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
694
695
    t::lib::Mocks::mock_preference('UseRecalls', 0);
696
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
697
698
    t::lib::Mocks::mock_preference("UseRecalls", 1);
699
    $item1->update({ notforloan => 1 });
700
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
701
702
    $item1->update({ notforloan => 0 });
703
    Koha::CirculationRules->set_rules({
704
        branchcode => $branchcode,
705
        categorycode => $patron1->categorycode,
706
        itemtype => $item1->effective_itemtype,
707
        rules => {
708
            recalls_allowed => 0,
709
            recalls_per_record => 1,
710
            on_shelf_recalls => 'all',
711
        },
712
    });
713
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
714
715
    Koha::CirculationRules->set_rules({
716
        branchcode => $branchcode,
717
        categorycode => $patron1->categorycode,
718
        itemtype => $item1->effective_itemtype,
719
        rules => {
720
            recalls_allowed => 1,
721
            recalls_per_record => 1,
722
            on_shelf_recalls => 'all',
723
        },
724
    });
725
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
726
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
727
728
    $recall1->set_cancelled;
729
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
730
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
731
732
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
733
734
    Koha::CirculationRules->set_rules({
735
        branchcode => $branchcode,
736
        categorycode => $patron1->categorycode,
737
        itemtype => $item1->effective_itemtype,
738
        rules => {
739
            recalls_allowed => 1,
740
            recalls_per_record => 1,
741
            on_shelf_recalls => 'any',
742
        },
743
    });
744
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
745
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
746
747
    $recall2->set_cancelled;
748
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
749
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
750
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
751
752
    $item1->update({ withdrawn => 1 });
753
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
754
755
    $schema->storage->txn_rollback;
756
};
(-)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 => 9;
22
use Test::More tests => 10;
23
use Test::Exception;
23
use Test::Exception;
24
24
25
use C4::Biblio qw( GetMarcSubfieldStructure );
25
use C4::Biblio qw( GetMarcSubfieldStructure );
Lines 816-818 subtest 'get_transfers' => sub { Link Here
816
816
817
    $schema->storage->txn_rollback;
817
    $schema->storage->txn_rollback;
818
};
818
};
819
820
subtest 'Recalls tests' => sub {
821
822
    plan tests => 20;
823
824
    $schema->storage->txn_begin;
825
826
    my $item1 = $builder->build_sample_item;
827
    my $biblio = $item1->biblio;
828
    my $branchcode = $item1->holdingbranch;
829
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
830
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
831
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
832
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
833
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
834
835
    my $recall1 = Koha::Recall->new({
836
        borrowernumber => $patron1->borrowernumber,
837
        recalldate => Koha::DateUtils::dt_from_string,
838
        biblionumber => $biblio->biblionumber,
839
        branchcode => $branchcode,
840
        status => 'R',
841
        itemnumber => $item1->itemnumber,
842
        expirationdate => undef,
843
        item_level_recall => 1
844
    })->store;
845
    my $recall2 = Koha::Recall->new({
846
        borrowernumber => $patron2->borrowernumber,
847
        recalldate => Koha::DateUtils::dt_from_string,
848
        biblionumber => $biblio->biblionumber,
849
        branchcode => $branchcode,
850
        status => 'R',
851
        itemnumber => $item1->itemnumber,
852
        expirationdate => undef,
853
        item_level_recall =>1
854
    })->store;
855
856
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
857
858
    $recall2->set_cancelled;
859
860
    t::lib::Mocks::mock_preference('UseRecalls', 0);
861
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
862
863
    t::lib::Mocks::mock_preference("UseRecalls", 1);
864
865
    $item1->update({ notforloan => 1 });
866
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
867
    $item1->update({ notforloan => 0, itemlost => 1 });
868
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
869
    $item1->update({ itemlost => 0, withdrawn => 1 });
870
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
871
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
872
873
    $item1->update({ withdrawn => 0 });
874
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
875
876
    Koha::CirculationRules->set_rules({
877
        branchcode => $branchcode,
878
        categorycode => $patron1->categorycode,
879
        itemtype => $item1->effective_itemtype,
880
        rules => {
881
            recalls_allowed => 0,
882
            recalls_per_record => 1,
883
            on_shelf_recalls => 'all',
884
        },
885
    });
886
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
887
888
    Koha::CirculationRules->set_rules({
889
        branchcode => $branchcode,
890
        categorycode => $patron1->categorycode,
891
        itemtype => $item1->effective_itemtype,
892
        rules => {
893
            recalls_allowed => 1,
894
            recalls_per_record => 1,
895
            on_shelf_recalls => 'all',
896
        },
897
    });
898
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
899
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
900
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
901
902
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
903
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
904
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
905
906
    $recall1->set_cancelled;
907
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
908
909
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
910
911
    Koha::CirculationRules->set_rules({
912
        branchcode => $branchcode,
913
        categorycode => $patron1->categorycode,
914
        itemtype => $item1->effective_itemtype,
915
        rules => {
916
            recalls_allowed => 1,
917
            recalls_per_record => 1,
918
            on_shelf_recalls => 'any',
919
        },
920
    });
921
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
922
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
923
924
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
925
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
926
927
    $recall1 = Koha::Recall->new({
928
        borrowernumber => $patron1->borrowernumber,
929
        recalldate => Koha::DateUtils::dt_from_string,
930
        biblionumber => $biblio->biblionumber,
931
        branchcode => $branchcode,
932
        status => 'R',
933
        itemnumber => undef,
934
        expirationdate => undef,
935
        item_level_recall => 0
936
    })->store;
937
938
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
939
940
    Koha::CirculationRules->set_rules({
941
        branchcode => undef,
942
        categorycode => undef,
943
        itemtype => $item1->effective_itemtype,
944
        rules => {
945
            recalls_allowed => 0,
946
            recalls_per_record => 1,
947
            on_shelf_recalls => 'any',
948
        },
949
    });
950
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
951
952
    Koha::CirculationRules->set_rules({
953
        branchcode => undef,
954
        categorycode => undef,
955
        itemtype => $item1->effective_itemtype,
956
        rules => {
957
            recalls_allowed => 1,
958
            recalls_per_record => 1,
959
            on_shelf_recalls => 'any',
960
        },
961
    });
962
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
963
964
    # check_recalls tests
965
966
    $recall1 = Koha::Recall->new({
967
        borrowernumber => $patron2->borrowernumber,
968
        recalldate => Koha::DateUtils::dt_from_string,
969
        biblionumber => $biblio->biblionumber,
970
        branchcode => $branchcode,
971
        status => 'R',
972
        itemnumber => $item1->itemnumber,
973
        expirationdate => undef,
974
        item_level_recall => 1
975
    })->store;
976
    $recall2 = Koha::Recall->new({
977
        borrowernumber => $patron1->borrowernumber,
978
        recalldate => Koha::DateUtils::dt_from_string,
979
        biblionumber => $biblio->biblionumber,
980
        branchcode => $branchcode,
981
        status => 'R',
982
        itemnumber => undef,
983
        expirationdate => undef,
984
        item_level_recall => 0
985
    })->store;
986
    $recall2->set_waiting({ item => $item1 });
987
988
    # return a waiting recall
989
    my $check_recall = $item1->check_recalls;
990
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
991
992
    $recall2->revert_waiting;
993
994
    # return recall based on recalldate
995
    $check_recall = $item1->check_recalls;
996
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
997
998
    $recall1->set_cancelled;
999
1000
    # return a biblio-level recall
1001
    $check_recall = $item1->check_recalls;
1002
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
1003
1004
    $recall2->set_cancelled;
1005
};
(-)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 => 8;
22
use Test::More tests => 9;
23
use Test::Exception;
23
use Test::Exception;
24
use Test::Warn;
24
use Test::Warn;
25
25
Lines 716-718 subtest 'can_log_into() tests' => sub { Link Here
716
716
717
    $schema->storage->txn_rollback;
717
    $schema->storage->txn_rollback;
718
};
718
};
719
720
subtest 'recalls() tests' => sub {
721
722
    plan tests => 2;
723
724
    $schema->storage->txn_begin;
725
726
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
727
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
728
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
729
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
730
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
731
732
    Koha::Recall->new({
733
        biblionumber => $biblio1->biblionumber,
734
        borrowernumber => $patron->borrowernumber,
735
        itemnumber => $item1->itemnumber,
736
        branchcode => $patron->branchcode,
737
        recalldate => dt_from_string,
738
        status => 'R',
739
        item_level_recall => 1,
740
    })->store;
741
    Koha::Recall->new({
742
        biblionumber => $biblio2->biblionumber,
743
        borrowernumber => $patron->borrowernumber,
744
        itemnumber => $item2->itemnumber,
745
        branchcode => $patron->branchcode,
746
        recalldate => dt_from_string,
747
        status => 'R',
748
        item_level_recall => 1,
749
    })->store;
750
    Koha::Recall->new({
751
        biblionumber => $biblio1->biblionumber,
752
        borrowernumber => $patron->borrowernumber,
753
        itemnumber => undef,
754
        branchcode => $patron->branchcode,
755
        recalldate => dt_from_string,
756
        status => 'R',
757
        item_level_recall => 0,
758
    })->store;
759
    my $recall = Koha::Recall->new({
760
        biblionumber => $biblio1->biblionumber,
761
        borrowernumber => $patron->borrowernumber,
762
        itemnumber => undef,
763
        branchcode => $patron->branchcode,
764
        recalldate => dt_from_string,
765
        status => 'R',
766
        item_level_recall => 0,
767
    })->store;
768
    $recall->set_cancelled;
769
770
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
771
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
772
773
    $schema->storage->txn_rollback;
774
};
(-)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 46-52 subtest 'transformMARCXML4XSLT tests' => sub { Link Here
46
};
46
};
47
47
48
subtest 'buildKohaItemsNamespace status tests' => sub {
48
subtest 'buildKohaItemsNamespace status tests' => sub {
49
    plan tests => 14;
49
    plan tests => 15;
50
50
51
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
51
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
52
52
Lines 124-130 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
124
        }
124
        }
125
    });
125
    });
126
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
126
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
127
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit");
127
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (holds)");
128
    $hold->cancel;
128
129
129
    $builder->build({ source => "TmpHoldsqueue", value => {
130
    $builder->build({ source => "TmpHoldsqueue", value => {
130
        itemnumber => $item->itemnumber
131
        itemnumber => $item->itemnumber
Lines 133-138 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
133
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
134
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
134
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
135
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
135
136
137
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
138
        biblionumber    => $item->biblionumber,
139
        itemnumber      => $item->itemnumber,
140
        branchcode      => $item->holdingbranch,
141
        status          => 'R',
142
    }});
143
    $recall->set_waiting;
144
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
145
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
136
146
137
};
147
};
138
148
139
- 

Return to bug 19532