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

(-)a/C4/Circulation.pm (-2 / +120 lines)
Lines 309-314 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
309
309
310
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
310
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
311
311
312
=item C<RecallPlacedAtHoldingBranch>
313
314
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.
315
316
=item C<RecallFound>
317
318
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
319
312
=back
320
=back
313
321
314
=back
322
=back
Lines 382-387 sub transferbook { Link Here
382
        $dotransfer = 1;
390
        $dotransfer = 1;
383
    }
391
    }
384
392
393
    # find recall
394
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
395
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
396
        # do a transfer if the recall branch is different to the item holding branch
397
        if ( $recall->branchcode eq $fbr ) {
398
            $dotransfer = 0;
399
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
400
        } else {
401
            $dotransfer = 1;
402
            $messages->{'RecallFound'} = $recall;
403
        }
404
    }
405
385
    #actually do the transfer....
406
    #actually do the transfer....
386
    if ($dotransfer) {
407
    if ($dotransfer) {
387
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
408
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 732-737 sticky due date is invalid or due date in the past Link Here
732
753
733
if the borrower borrows to much things
754
if the borrower borrows to much things
734
755
756
=head3 RECALLED
757
758
recalled by someone else
759
735
=cut
760
=cut
736
761
737
sub CanBookBeIssued {
762
sub CanBookBeIssued {
Lines 1105-1111 sub CanBookBeIssued { Link Here
1105
        }
1130
        }
1106
    }
1131
    }
1107
1132
1108
    unless ( $ignore_reserves ) {
1133
    my $recall;
1134
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1135
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1136
    # Don't look at recalls that are in transit
1137
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1138
        my @recalls = $biblio->recalls;
1139
1140
        foreach my $r ( @recalls ) {
1141
            if ( $r->itemnumber and
1142
                $r->itemnumber == $item_object->itemnumber and
1143
                $r->borrowernumber == $patron->borrowernumber and
1144
                $r->waiting ) {
1145
                $messages{RECALLED} = $r->recall_id;
1146
                $recall = $r;
1147
                # this item is already waiting for this borrower and the recall can be fulfilled
1148
                last;
1149
            }
1150
            elsif ( $r->itemnumber and
1151
                $r->itemnumber == $item_object->itemnumber and
1152
                $r->in_transit ) {
1153
                # recalled item is in transit
1154
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1155
            }
1156
            elsif ( $r->item_level_recall and
1157
                $r->itemnumber == $item_object->itemnumber and
1158
                $r->borrowernumber != $patron->borrowernumber and
1159
                !$r->in_transit ) {
1160
                # this specific item has been recalled by a different patron
1161
                $needsconfirmation{RECALLED} = $r;
1162
                $recall = $r;
1163
                last;
1164
            }
1165
            elsif ( !$r->item_level_recall and
1166
                $r->borrowernumber != $patron->borrowernumber and
1167
                !$r->in_transit ) {
1168
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1169
                $needsconfirmation{RECALLED} = $r;
1170
                $recall = $r;
1171
                last;
1172
            }
1173
        }
1174
    }
1175
1176
    unless ( $ignore_reserves and defined $recall ) {
1109
        # See if the item is on reserve.
1177
        # See if the item is on reserve.
1110
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1178
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1111
        if ($restype) {
1179
        if ($restype) {
Lines 1420-1425 AddIssue does the following things : Link Here
1420
          * RESERVE PLACED ?
1488
          * RESERVE PLACED ?
1421
              - fill reserve if reserve to this patron
1489
              - fill reserve if reserve to this patron
1422
              - cancel reserve or not, otherwise
1490
              - cancel reserve or not, otherwise
1491
          * RECALL PLACED ?
1492
              - fill recall if recall to this patron
1493
              - cancel recall or not
1494
              - revert recall's waiting status or not
1423
          * TRANSFERT PENDING ?
1495
          * TRANSFERT PENDING ?
1424
              - complete the transfert
1496
              - complete the transfert
1425
          * ISSUE THE BOOK
1497
          * ISSUE THE BOOK
Lines 1434-1439 sub AddIssue { Link Here
1434
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1506
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1435
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1507
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1436
    my $auto_renew = $params && $params->{auto_renew};
1508
    my $auto_renew = $params && $params->{auto_renew};
1509
    my $cancel_recall = $params && $params->{cancel_recall};
1510
    my $recall_id = $params && $params->{recall_id};
1437
    my $dbh          = C4::Context->dbh;
1511
    my $dbh          = C4::Context->dbh;
1438
    my $barcodecheck = CheckValidBarcode($barcode);
1512
    my $barcodecheck = CheckValidBarcode($barcode);
1439
1513
Lines 1507-1512 sub AddIssue { Link Here
1507
                $item_object->discard_changes;
1581
                $item_object->discard_changes;
1508
            }
1582
            }
1509
1583
1584
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1585
1510
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1586
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1511
1587
1512
            # Starting process for transfer job (checking transfert and validate it if we have one)
1588
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1954-1959 Value 1 if return is successful. Link Here
1954
2030
1955
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2031
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1956
2032
2033
=item C<RecallFound>
2034
2035
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2036
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2037
branchcode.
2038
2039
=item C<TransferredRecall>
2040
2041
This item has been transferred to this branch to fill a recall. The recall object is returned.
2042
1957
=back
2043
=back
1958
2044
1959
C<$iteminformation> is a reference-to-hash, giving information about the
2045
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2218-2223 sub AddReturn { Link Here
2218
        }
2304
        }
2219
    }
2305
    }
2220
2306
2307
    # find recalls...
2308
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2309
    my $recall = undef;
2310
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2311
    if ( defined $recall ) {
2312
        $messages->{RecallFound} = $recall;
2313
        if ( $recall->branchcode ne $branch ) {
2314
            $messages->{RecallNeedsTransfer} = $branch;
2315
        }
2316
    }
2317
2221
    # find reserves.....
2318
    # find reserves.....
2222
    # launch the Checkreserves routine to find any holds
2319
    # launch the Checkreserves routine to find any holds
2223
    my ($resfound, $resrec);
2320
    my ($resfound, $resrec);
Lines 2276-2288 sub AddReturn { Link Here
2276
        $request->status('RET') if $request;
2373
        $request->status('RET') if $request;
2277
    }
2374
    }
2278
2375
2376
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2377
    if ( $transfer_recall and
2378
         $transfer_recall->branchcode eq $branch and
2379
         C4::Context->preference('UseRecalls') ) {
2380
        $messages->{TransferredRecall} = $transfer_recall;
2381
    }
2382
2279
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2383
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2280
    if ( $validTransfer && !$is_in_rotating_collection
2384
    if ( $validTransfer && !$is_in_rotating_collection
2281
        && ( $doreturn or $messages->{'NotIssued'} )
2385
        && ( $doreturn or $messages->{'NotIssued'} )
2282
        and !$resfound
2386
        and !$resfound
2283
        and ( $branch ne $returnbranch )
2387
        and ( $branch ne $returnbranch )
2284
        and not $messages->{'WrongTransfer'}
2388
        and not $messages->{'WrongTransfer'}
2285
        and not $messages->{'WasTransfered'} )
2389
        and not $messages->{'WasTransfered'}
2390
        and not $messages->{TransferredRecall}
2391
        and not $messages->{RecallNeedsTransfer} )
2286
    {
2392
    {
2287
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2393
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2288
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2394
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
Lines 2871-2876 sub CanBookBeRenewed { Link Here
2871
        }
2977
        }
2872
    }
2978
    }
2873
2979
2980
    my $recall = undef;
2981
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2982
    if ( defined $recall ) {
2983
        if ( $recall->item_level_recall ) {
2984
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2985
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2986
        } else {
2987
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2988
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2989
        }
2990
    }
2991
2874
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2992
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2875
2993
2876
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2994
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
(-)a/C4/Reserves.pm (+5 lines)
Lines 362-367 sub CanBookBeReserved{ Link Here
362
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
362
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
363
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
363
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
364
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
364
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
365
         { status => recall }, if the borrower has already placed a recall on this item
365
366
366
=cut
367
=cut
367
368
Lines 399-404 sub CanItemBeReserved { Link Here
399
        return { status =>'alreadypossession' };
400
        return { status =>'alreadypossession' };
400
    }
401
    }
401
402
403
    # check if a recall exists on this item from this borrower
404
    return { status => 'recall' }
405
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
406
402
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
407
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
403
408
404
    my $querycount = q{
409
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1818-1823 sub searchResults { Link Here
1818
        my $can_place_holds       = 0;
1818
        my $can_place_holds       = 0;
1819
        my $item_onhold_count     = 0;
1819
        my $item_onhold_count     = 0;
1820
        my $notforloan_count      = 0;
1820
        my $notforloan_count      = 0;
1821
        my $item_recalled_count   = 0;
1821
        my $items_count           = scalar(@fields);
1822
        my $items_count           = scalar(@fields);
1822
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1823
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1823
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1824
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1912-1917 sub searchResults { Link Here
1912
                # is item on the reserve shelf?
1913
                # is item on the reserve shelf?
1913
                my $reservestatus = '';
1914
                my $reservestatus = '';
1914
1915
1916
                # is item a waiting recall?
1917
                my $recallstatus = '';
1918
1915
                unless ($item->{withdrawn}
1919
                unless ($item->{withdrawn}
1916
                        || $item->{itemlost}
1920
                        || $item->{itemlost}
1917
                        || $item->{damaged}
1921
                        || $item->{damaged}
Lines 1933-1938 sub searchResults { Link Here
1933
                    #
1937
                    #
1934
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1938
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1935
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1939
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1940
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
1936
                }
1941
                }
1937
1942
1938
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
1943
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 1941-1946 sub searchResults { Link Here
1941
                    || $item->{damaged}
1946
                    || $item->{damaged}
1942
                    || $item->{notforloan}
1947
                    || $item->{notforloan}
1943
                    || $reservestatus eq 'Waiting'
1948
                    || $reservestatus eq 'Waiting'
1949
                    || $recallstatus eq 'Waiting'
1944
                    || ($transfertwhen && $transfertwhen ne ''))
1950
                    || ($transfertwhen && $transfertwhen ne ''))
1945
                {
1951
                {
1946
                    $withdrawn_count++        if $item->{withdrawn};
1952
                    $withdrawn_count++        if $item->{withdrawn};
Lines 1948-1953 sub searchResults { Link Here
1948
                    $itemdamaged_count++     if $item->{damaged};
1954
                    $itemdamaged_count++     if $item->{damaged};
1949
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1955
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1950
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1956
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1957
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
1951
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1958
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1952
1959
1953
                    # can place a hold on a item if
1960
                    # can place a hold on a item if
Lines 1969-1974 sub searchResults { Link Here
1969
                        $other_items->{$key}->{$_} = $item->{$_};
1976
                        $other_items->{$key}->{$_} = $item->{$_};
1970
                    }
1977
                    }
1971
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1978
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1979
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
1972
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1980
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1973
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1981
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1974
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
1982
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2043-2048 sub searchResults { Link Here
2043
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2051
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2044
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2052
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2045
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2053
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2054
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2046
        $oldbiblio->{orderedcount}         = $ordered_count;
2055
        $oldbiblio->{orderedcount}         = $ordered_count;
2047
        $oldbiblio->{notforloancount}      = $notforloan_count;
2056
        $oldbiblio->{notforloancount}      = $notforloan_count;
2048
2057
(-)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 887-892 sub to_api_mapping { Link Here
887
    };
887
    };
888
}
888
}
889
889
890
=head3 recalls
891
892
    my @recalls = $biblio->recalls;
893
894
Return all active recalls attached to this biblio, sorted by oldest first
895
896
=cut
897
898
sub recalls {
899
    my ( $self ) = @_;
900
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
901
    return @recalls_rs;
902
}
903
904
=head3 can_be_recalled
905
906
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
907
908
Does biblio-level checks and returns the items attached to this biblio that are available for recall
909
910
=cut
911
912
sub can_be_recalled {
913
    my ( $self, $params ) = @_;
914
915
    return 0 if !( C4::Context->preference('UseRecalls') );
916
917
    my $patron = $params->{patron};
918
919
    my $branchcode = C4::Context->userenv->{'branch'};
920
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
921
        $branchcode = $patron->branchcode;
922
    }
923
924
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
925
926
    # if there are no available items at all, no recall can be placed
927
    return 0 if ( scalar @all_items == 0 );
928
929
    my @itemtypes;
930
    my @itemnumbers;
931
    my @items;
932
    foreach my $item ( @all_items ) {
933
        if ( $item->can_be_recalled({ patron => $patron }) ) {
934
            push( @itemtypes, $item->effective_itemtype );
935
            push( @itemnumbers, $item->itemnumber );
936
            push( @items, $item );
937
        }
938
    }
939
940
    # if there are no recallable items, no recall can be placed
941
    return 0 if ( scalar @items == 0 );
942
943
    # Check the circulation rule for each relevant itemtype for this biblio
944
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
945
    foreach my $itemtype ( @itemtypes ) {
946
        my $rule = Koha::CirculationRules->get_effective_rules({
947
            branchcode => $branchcode,
948
            categorycode => $patron ? $patron->categorycode : undef,
949
            itemtype => $itemtype,
950
            rules => [
951
                'recalls_allowed',
952
                'recalls_per_record',
953
                'on_shelf_recalls',
954
            ],
955
        });
956
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
957
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
958
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
959
    }
960
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
961
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
962
    my %on_shelf_recalls_count = ();
963
    foreach my $count ( @on_shelf_recalls ) {
964
        $on_shelf_recalls_count{$count}++;
965
    }
966
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
967
968
    # check recalls allowed has been set and is not zero
969
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
970
971
    if ( $patron ) {
972
        # check borrower has not reached open recalls allowed limit
973
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
974
975
        # check borrower has not reached open recalls allowed per record limit
976
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
977
978
        # check if any of the items under this biblio are already checked out by this borrower
979
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
980
    }
981
982
    # check item availability
983
    my $checked_out_count = 0;
984
    foreach (@items) {
985
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
986
    }
987
988
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
989
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
990
991
    # can't recall if no items have been checked out
992
    return 0 if ( $checked_out_count == 0 );
993
994
    # can recall
995
    return @items;
996
}
997
890
=head2 Internal methods
998
=head2 Internal methods
891
999
892
=head3 type
1000
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1166-1171 sub _after_item_action_hooks { Link Here
1166
    );
1166
    );
1167
}
1167
}
1168
1168
1169
=head3 recall
1170
1171
    my $recall = $item->recall;
1172
1173
Return the relevant recall for this item
1174
1175
=cut
1176
1177
sub recall {
1178
    my ( $self ) = @_;
1179
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1180
    foreach my $recall (@recalls) {
1181
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1182
            return $recall;
1183
        }
1184
    }
1185
    # no item-level recall to return, so return earliest biblio-level
1186
    # FIXME: eventually this will be based on priority
1187
    return $recalls[0];
1188
}
1189
1190
=head3 can_be_recalled
1191
1192
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1193
1194
Does item-level checks and returns if items can be recalled by this borrower
1195
1196
=cut
1197
1198
sub can_be_recalled {
1199
    my ( $self, $params ) = @_;
1200
1201
    return 0 if !( C4::Context->preference('UseRecalls') );
1202
1203
    # check if this item is not for loan, withdrawn or lost
1204
    return 0 if ( $self->notforloan != 0 );
1205
    return 0 if ( $self->itemlost != 0 );
1206
    return 0 if ( $self->withdrawn != 0 );
1207
1208
    # check if this item is not checked out - if not checked out, can't be recalled
1209
    return 0 if ( !defined( $self->checkout ) );
1210
1211
    my $patron = $params->{patron};
1212
1213
    my $branchcode = C4::Context->userenv->{'branch'};
1214
    if ( $patron ) {
1215
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1216
    }
1217
1218
    # Check the circulation rule for each relevant itemtype for this item
1219
    my $rule = Koha::CirculationRules->get_effective_rules({
1220
        branchcode => $branchcode,
1221
        categorycode => $patron ? $patron->categorycode : undef,
1222
        itemtype => $self->effective_itemtype,
1223
        rules => [
1224
            'recalls_allowed',
1225
            'recalls_per_record',
1226
            'on_shelf_recalls',
1227
        ],
1228
    });
1229
1230
    # check recalls allowed has been set and is not zero
1231
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1232
1233
    if ( $patron ) {
1234
        # check borrower has not reached open recalls allowed limit
1235
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1236
1237
        # check borrower has not reach open recalls allowed per record limit
1238
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1239
1240
        # check if this patron has already recalled this item
1241
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1242
1243
        # check if this patron has already checked out this item
1244
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1245
1246
        # check if this patron has already reserved this item
1247
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1248
    }
1249
1250
    # check item availability
1251
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1252
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1253
1254
    # if there are no available items at all, no recall can be placed
1255
    return 0 if ( scalar @items == 0 );
1256
1257
    my $checked_out_count = 0;
1258
    foreach (@items) {
1259
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1260
    }
1261
1262
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1263
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1264
1265
    # can't recall if no items have been checked out
1266
    return 0 if ( $checked_out_count == 0 );
1267
1268
    # can recall
1269
    return 1;
1270
}
1271
1272
=head3 can_be_waiting_recall
1273
1274
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1275
1276
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1277
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1278
1279
=cut
1280
1281
sub can_be_waiting_recall {
1282
    my ( $self ) = @_;
1283
1284
    return 0 if !( C4::Context->preference('UseRecalls') );
1285
1286
    # check if this item is not for loan, withdrawn or lost
1287
    return 0 if ( $self->notforloan != 0 );
1288
    return 0 if ( $self->itemlost != 0 );
1289
    return 0 if ( $self->withdrawn != 0 );
1290
1291
    my $branchcode = $self->holdingbranch;
1292
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1293
        $branchcode = C4::Context->userenv->{'branch'};
1294
    } else {
1295
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1296
    }
1297
1298
    # Check the circulation rule for each relevant itemtype for this item
1299
    my $rule = Koha::CirculationRules->get_effective_rules({
1300
        branchcode => $branchcode,
1301
        categorycode => undef,
1302
        itemtype => $self->effective_itemtype,
1303
        rules => [
1304
            'recalls_allowed',
1305
        ],
1306
    });
1307
1308
    # check recalls allowed has been set and is not zero
1309
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1310
1311
    # can recall
1312
    return 1;
1313
}
1314
1315
=head3 check_recalls
1316
1317
    my $recall = $item->check_recalls;
1318
1319
Get the most relevant recall for this item.
1320
1321
=cut
1322
1323
sub check_recalls {
1324
    my ( $self ) = @_;
1325
1326
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1327
1328
    my $recall;
1329
    # iterate through relevant recalls to find the best one.
1330
    # if we come across a waiting recall, use this one.
1331
    # 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.
1332
    foreach my $r ( @recalls ) {
1333
        if ( $r->waiting ) {
1334
            $recall = $r;
1335
            last;
1336
        }
1337
    }
1338
    unless ( defined $recall ) {
1339
        $recall = $recalls[0];
1340
    }
1341
1342
    return $recall;
1343
}
1344
1169
=head3 _type
1345
=head3 _type
1170
1346
1171
=cut
1347
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1792-1797 sub to_api_mapping { Link Here
1792
    };
1792
    };
1793
}
1793
}
1794
1794
1795
=head3 recalls
1796
1797
    my $recalls = $patron->recalls;
1798
1799
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1800
1801
Return the patron's active recalls - total, or on a specific biblio
1802
1803
=cut
1804
1805
sub recalls {
1806
    my ( $self, $params ) = @_;
1807
1808
    my $biblionumber = $params->{biblionumber};
1809
1810
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1811
1812
    if ( $biblionumber ) {
1813
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1814
    }
1815
1816
    return $recalls_rs;
1817
}
1818
1795
=head2 Internal methods
1819
=head2 Internal methods
1796
1820
1797
=head3 _type
1821
=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 => 53;
21
use Test::More tests => 56;
22
use Test::Exception;
22
use Test::Exception;
23
use Test::MockModule;
23
use Test::MockModule;
24
use Test::Deep qw( cmp_deeply );
24
use Test::Deep qw( cmp_deeply );
Lines 329-335 subtest "GetIssuingCharges tests" => sub { Link Here
329
329
330
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
330
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
331
subtest "CanBookBeRenewed tests" => sub {
331
subtest "CanBookBeRenewed tests" => sub {
332
    plan tests => 95;
332
    plan tests => 99;
333
333
334
    C4::Context->set_preference('ItemsDeniedRenewal','');
334
    C4::Context->set_preference('ItemsDeniedRenewal','');
335
    # Generate test biblio
335
    # Generate test biblio
Lines 1355-1360 subtest "CanBookBeRenewed tests" => sub { Link Here
1355
            $item_3->itemcallnumber || '' ),
1355
            $item_3->itemcallnumber || '' ),
1356
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1356
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1357
    );
1357
    );
1358
1359
    # Recalls
1360
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1361
    Koha::CirculationRules->set_rules({
1362
        categorycode => undef,
1363
        branchcode => undef,
1364
        itemtype => undef,
1365
        rules => {
1366
            recalls_allowed => 10,
1367
            renewalsallowed => 5,
1368
        },
1369
    });
1370
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1371
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1372
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1373
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1374
1375
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1376
1377
    # item-level and this item: renewal not allowed
1378
    my $recall = Koha::Recall->new({
1379
        biblionumber => $recall_item1->biblionumber,
1380
        itemnumber => $recall_item1->itemnumber,
1381
        borrowernumber => $recall_borrower->borrowernumber,
1382
        branchcode => $recall_borrower->branchcode,
1383
        item_level_recall => 1,
1384
        status => 'R',
1385
    })->store;
1386
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1387
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1388
    $recall->set_cancelled;
1389
1390
    # biblio-level requested recall: renewal not allowed
1391
    $recall = Koha::Recall->new({
1392
        biblionumber => $recall_item1->biblionumber,
1393
        itemnumber => undef,
1394
        borrowernumber => $recall_borrower->borrowernumber,
1395
        branchcode => $recall_borrower->branchcode,
1396
        item_level_recall => 0,
1397
        status => 'R',
1398
    })->store;
1399
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1400
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1401
    $recall->set_cancelled;
1402
1403
    # item-level and not this item: renewal allowed
1404
    $recall = Koha::Recall->new({
1405
        biblionumber => $recall_item2->biblionumber,
1406
        itemnumber => $recall_item2->itemnumber,
1407
        borrowernumber => $recall_borrower->borrowernumber,
1408
        branchcode => $recall_borrower->branchcode,
1409
        item_level_recall => 1,
1410
        status => 'R',
1411
    })->store;
1412
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1413
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1414
    $recall->set_cancelled;
1415
1416
    # biblio-level waiting recall: renewal allowed
1417
    $recall = Koha::Recall->new({
1418
        biblionumber => $recall_item1->biblionumber,
1419
        itemnumber => undef,
1420
        borrowernumber => $recall_borrower->borrowernumber,
1421
        branchcode => $recall_borrower->branchcode,
1422
        item_level_recall => 0,
1423
        status => 'R',
1424
    })->store;
1425
    $recall->set_waiting({ item => $recall_item1 });
1426
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1427
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1428
    $recall->set_cancelled;
1358
};
1429
};
1359
1430
1360
subtest "GetUpcomingDueIssues" => sub {
1431
subtest "GetUpcomingDueIssues" => sub {
Lines 1835-1840 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1835
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1906
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1836
};
1907
};
1837
1908
1909
subtest 'AddIssue | recalls' => sub {
1910
    plan tests => 3;
1911
1912
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1913
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1914
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1915
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1916
    my $item = $builder->build_sample_item;
1917
    Koha::CirculationRules->set_rules({
1918
        branchcode => undef,
1919
        itemtype => undef,
1920
        categorycode => undef,
1921
        rules => {
1922
            recalls_allowed => 10,
1923
        },
1924
    });
1925
1926
    # checking out item that they have recalled
1927
    my $recall1 = Koha::Recall->new({
1928
        borrowernumber => $patron1->borrowernumber,
1929
        biblionumber => $item->biblionumber,
1930
        itemnumber => $item->itemnumber,
1931
        item_level_recall => 1,
1932
        branchcode => $patron1->branchcode,
1933
        status => 'R',
1934
    })->store;
1935
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1936
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1937
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1938
    AddReturn( $item->barcode, $item->homebranch );
1939
1940
    # this item is has a recall request. cancel recall
1941
    my $recall2 = Koha::Recall->new({
1942
        borrowernumber => $patron2->borrowernumber,
1943
        biblionumber => $item->biblionumber,
1944
        itemnumber => $item->itemnumber,
1945
        item_level_recall => 1,
1946
        branchcode => $patron2->branchcode,
1947
        status => 'R',
1948
    })->store;
1949
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1950
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1951
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1952
    AddReturn( $item->barcode, $item->homebranch );
1953
1954
    # this item is waiting to fulfill a recall. revert recall
1955
    my $recall3 = Koha::Recall->new({
1956
        borrowernumber => $patron2->borrowernumber,
1957
        biblionumber => $item->biblionumber,
1958
        itemnumber => $item->itemnumber,
1959
        item_level_recall => 1,
1960
        branchcode => $patron2->branchcode,
1961
        status => 'R',
1962
    })->store;
1963
    $recall3->set_waiting;
1964
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1965
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1966
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1967
    AddReturn( $item->barcode, $item->homebranch );
1968
};
1969
1970
1838
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1971
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1839
    plan tests => 8;
1972
    plan tests => 8;
1840
1973
Lines 3739-3744 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3739
    # TODO test with AllowNotForLoanOverride = 1
3872
    # TODO test with AllowNotForLoanOverride = 1
3740
};
3873
};
3741
3874
3875
subtest 'CanBookBeIssued | recalls' => sub {
3876
    plan tests => 3;
3877
3878
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3879
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3880
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3881
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3882
    my $item = $builder->build_sample_item;
3883
    Koha::CirculationRules->set_rules({
3884
        branchcode => undef,
3885
        itemtype => undef,
3886
        categorycode => undef,
3887
        rules => {
3888
            recalls_allowed => 10,
3889
        },
3890
    });
3891
3892
    # item-level recall
3893
    my $recall = Koha::Recall->new({
3894
        borrowernumber => $patron1->borrowernumber,
3895
        biblionumber => $item->biblionumber,
3896
        itemnumber => $item->itemnumber,
3897
        item_level_recall => 1,
3898
        branchcode => $patron1->branchcode,
3899
        status => 'R',
3900
    })->store;
3901
3902
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3903
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3904
3905
    $recall->set_cancelled;
3906
3907
    # biblio-level recall
3908
    $recall = Koha::Recall->new({
3909
        borrowernumber => $patron1->borrowernumber,
3910
        biblionumber => $item->biblionumber,
3911
        itemnumber => undef,
3912
        item_level_recall => 0,
3913
        branchcode => $patron1->branchcode,
3914
        status => 'R',
3915
    })->store;
3916
3917
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3918
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3919
3920
    $recall->set_cancelled;
3921
3922
    # biblio-level recall
3923
    $recall = Koha::Recall->new({
3924
        borrowernumber => $patron1->borrowernumber,
3925
        biblionumber => $item->biblionumber,
3926
        itemnumber => undef,
3927
        item_level_recall => 0,
3928
        branchcode => $patron1->branchcode,
3929
        status => 'R',
3930
    })->store;
3931
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
3932
3933
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
3934
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
3935
3936
    $recall->set_cancelled;
3937
};
3938
3742
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3939
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3743
    plan tests => 1;
3940
    plan tests => 1;
3744
3941
Lines 3754-3759 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3754
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3951
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3755
};
3952
};
3756
3953
3954
subtest 'AddReturn | recalls' => sub {
3955
    plan tests => 3;
3956
3957
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3958
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3959
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3960
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3961
    my $item1 = $builder->build_sample_item;
3962
    Koha::CirculationRules->set_rules({
3963
        branchcode => undef,
3964
        itemtype => undef,
3965
        categorycode => undef,
3966
        rules => {
3967
            recalls_allowed => 10,
3968
        },
3969
    });
3970
3971
    # this item can fill a recall with pickup at this branch
3972
    AddIssue( $patron1->unblessed, $item1->barcode );
3973
    my $recall1 = Koha::Recall->new({
3974
        borrowernumber => $patron2->borrowernumber,
3975
        biblionumber => $item1->biblionumber,
3976
        itemnumber => $item1->itemnumber,
3977
        item_level_recall => 1,
3978
        branchcode => $item1->homebranch,
3979
        status => 'R',
3980
    })->store;
3981
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3982
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3983
    $recall1->set_cancelled;
3984
3985
    # this item can fill a recall but needs transfer
3986
    AddIssue( $patron1->unblessed, $item1->barcode );
3987
    $recall1 = Koha::Recall->new({
3988
        borrowernumber => $patron2->borrowernumber,
3989
        biblionumber => $item1->biblionumber,
3990
        itemnumber => $item1->itemnumber,
3991
        item_level_recall => 1,
3992
        branchcode => $patron2->branchcode,
3993
        status => 'R',
3994
    })->store;
3995
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3996
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3997
    $recall1->set_cancelled;
3998
3999
    # this item is already in transit, do not ask to transfer
4000
    AddIssue( $patron1->unblessed, $item1->barcode );
4001
    $recall1 = Koha::Recall->new({
4002
        borrowernumber => $patron2->borrowernumber,
4003
        biblionumber => $item1->biblionumber,
4004
        itemnumber => $item1->itemnumber,
4005
        item_level_recall => 1,
4006
        branchcode => $patron2->branchcode,
4007
        status => 'R',
4008
    })->store;
4009
    $recall1->start_transfer;
4010
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
4011
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
4012
    $recall1->set_cancelled;
4013
};
3757
4014
3758
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
4015
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3759
4016
(-)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, 1, 'Transfer of reserved item succeeded without ignore reserves' );
154
    is( $dotransfer, 1, 'Transfer of reserved item succeeded 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 305-307 subtest 'transferbook test from branch' => sub { Link Here
305
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
335
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
306
336
307
};
337
};
338
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (+27 lines)
Lines 1326-1332 subtest 'non priority holds' => sub { Link Here
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1327
1327
1328
    $schema->storage->txn_rollback;
1328
    $schema->storage->txn_rollback;
1329
};
1330
1331
subtest 'CanItemBeReserved / recall' => sub {
1332
    plan tests => 1;
1333
1334
    $schema->storage->txn_begin;
1335
1336
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1337
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1338
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1339
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1340
    my $item1   = $builder->build_sample_item(
1341
        {
1342
            biblionumber => $biblio1->biblionumber,
1343
            library      => $library1->branchcode
1344
        }
1345
    );
1346
    Koha::Recall->new({
1347
        borrowernumber => $patron1->borrowernumber,
1348
        biblionumber => $biblio1->biblionumber,
1349
        branchcode => $library1->branchcode,
1350
        itemnumber => $item1->itemnumber,
1351
        recalldate => '2020-05-04 10:10:10',
1352
        item_level_recall => 1,
1353
    })->store;
1354
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1329
1355
1356
    $schema->storage->txn_rollback;
1330
};
1357
};
1331
1358
1332
subtest 'CanItemBeReserved rule precedence tests' => sub {
1359
subtest 'CanItemBeReserved rule precedence tests' => sub {
(-)a/t/db_dependent/Koha/Biblio.t (-1 / +118 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 14;
20
use Test::More tests => 15;
21
21
22
use C4::Biblio;
22
use C4::Biblio;
23
use Koha::Database;
23
use Koha::Database;
Lines 610-612 subtest 'get_marc_notes() UNIMARC tests' => sub { Link Here
610
610
611
    $schema->storage->txn_rollback;
611
    $schema->storage->txn_rollback;
612
};
612
};
613
614
subtest 'Recalls tests' => sub {
615
616
    plan tests => 12;
617
618
    $schema->storage->txn_begin;
619
    my $item1 = $builder->build_sample_item;
620
    my $biblio = $item1->biblio;
621
    my $branchcode = $item1->holdingbranch;
622
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
623
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
624
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
625
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
626
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
627
628
    my $recall1 = Koha::Recall->new({
629
        borrowernumber => $patron1->borrowernumber,
630
        recalldate => Koha::DateUtils::dt_from_string,
631
        biblionumber => $biblio->biblionumber,
632
        branchcode => $branchcode,
633
        status => 'R',
634
        itemnumber => $item1->itemnumber,
635
        expirationdate => undef,
636
        item_level_recall => 1
637
    })->store;
638
    my $recall2 = Koha::Recall->new({
639
        borrowernumber => $patron2->borrowernumber,
640
        recalldate => Koha::DateUtils::dt_from_string,
641
        biblionumber => $biblio->biblionumber,
642
        branchcode => $branchcode,
643
        status => 'R',
644
        itemnumber => undef,
645
        expirationdate => undef,
646
        item_level_recall => 0
647
    })->store;
648
    my $recall3 = Koha::Recall->new({
649
        borrowernumber => $patron3->borrowernumber,
650
        recalldate => Koha::DateUtils::dt_from_string,
651
        biblionumber => $biblio->biblionumber,
652
        branchcode => $branchcode,
653
        status => 'R',
654
        itemnumber => $item1->itemnumber,
655
        expirationdate => undef,
656
        item_level_recall => 1
657
    })->store;
658
659
    my $recalls_count = scalar $biblio->recalls;
660
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
661
662
    $recall1->set_cancelled;
663
    $recall2->set_expired({ interface => 'COMMANDLINE' });
664
665
    $recalls_count = scalar $biblio->recalls;
666
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
667
668
    t::lib::Mocks::mock_preference('UseRecalls', 0);
669
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
670
671
    t::lib::Mocks::mock_preference("UseRecalls", 1);
672
    $item1->update({ notforloan => 1 });
673
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
674
675
    $item1->update({ notforloan => 0 });
676
    Koha::CirculationRules->set_rules({
677
        branchcode => $branchcode,
678
        categorycode => $patron1->categorycode,
679
        itemtype => $item1->effective_itemtype,
680
        rules => {
681
            recalls_allowed => 0,
682
            recalls_per_record => 1,
683
            on_shelf_recalls => 'all',
684
        },
685
    });
686
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
687
688
    Koha::CirculationRules->set_rules({
689
        branchcode => $branchcode,
690
        categorycode => $patron1->categorycode,
691
        itemtype => $item1->effective_itemtype,
692
        rules => {
693
            recalls_allowed => 1,
694
            recalls_per_record => 1,
695
            on_shelf_recalls => 'all',
696
        },
697
    });
698
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
699
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
700
701
    $recall1->set_cancelled;
702
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
703
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
704
705
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
706
707
    Koha::CirculationRules->set_rules({
708
        branchcode => $branchcode,
709
        categorycode => $patron1->categorycode,
710
        itemtype => $item1->effective_itemtype,
711
        rules => {
712
            recalls_allowed => 1,
713
            recalls_per_record => 1,
714
            on_shelf_recalls => 'any',
715
        },
716
    });
717
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
718
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
719
720
    $recall2->set_cancelled;
721
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
722
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
723
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
724
725
    $item1->update({ withdrawn => 1 });
726
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
727
728
    $schema->storage->txn_rollback;
729
};
(-)a/t/db_dependent/Koha/Item.t (+187 lines)
Lines 691-693 subtest 'Tests for itemtype' => sub { Link Here
691
691
692
    $schema->storage->txn_rollback;
692
    $schema->storage->txn_rollback;
693
};
693
};
694
695
subtest 'Recalls tests' => sub {
696
697
    plan tests => 20;
698
699
    $schema->storage->txn_begin;
700
701
    my $item1 = $builder->build_sample_item;
702
    my $biblio = $item1->biblio;
703
    my $branchcode = $item1->holdingbranch;
704
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
705
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
706
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
707
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
708
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
709
710
    my $recall1 = Koha::Recall->new({
711
        borrowernumber => $patron1->borrowernumber,
712
        recalldate => Koha::DateUtils::dt_from_string,
713
        biblionumber => $biblio->biblionumber,
714
        branchcode => $branchcode,
715
        status => 'R',
716
        itemnumber => $item1->itemnumber,
717
        expirationdate => undef,
718
        item_level_recall => 1
719
    })->store;
720
    my $recall2 = Koha::Recall->new({
721
        borrowernumber => $patron2->borrowernumber,
722
        recalldate => Koha::DateUtils::dt_from_string,
723
        biblionumber => $biblio->biblionumber,
724
        branchcode => $branchcode,
725
        status => 'R',
726
        itemnumber => $item1->itemnumber,
727
        expirationdate => undef,
728
        item_level_recall =>1
729
    })->store;
730
731
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
732
733
    $recall2->set_cancelled;
734
735
    t::lib::Mocks::mock_preference('UseRecalls', 0);
736
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
737
738
    t::lib::Mocks::mock_preference("UseRecalls", 1);
739
740
    $item1->update({ notforloan => 1 });
741
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
742
    $item1->update({ notforloan => 0, itemlost => 1 });
743
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
744
    $item1->update({ itemlost => 0, withdrawn => 1 });
745
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
746
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
747
748
    $item1->update({ withdrawn => 0 });
749
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
750
751
    Koha::CirculationRules->set_rules({
752
        branchcode => $branchcode,
753
        categorycode => $patron1->categorycode,
754
        itemtype => $item1->effective_itemtype,
755
        rules => {
756
            recalls_allowed => 0,
757
            recalls_per_record => 1,
758
            on_shelf_recalls => 'all',
759
        },
760
    });
761
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
762
763
    Koha::CirculationRules->set_rules({
764
        branchcode => $branchcode,
765
        categorycode => $patron1->categorycode,
766
        itemtype => $item1->effective_itemtype,
767
        rules => {
768
            recalls_allowed => 1,
769
            recalls_per_record => 1,
770
            on_shelf_recalls => 'all',
771
        },
772
    });
773
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
774
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
775
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
776
777
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
778
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
779
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
780
781
    $recall1->set_cancelled;
782
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
783
784
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
785
786
    Koha::CirculationRules->set_rules({
787
        branchcode => $branchcode,
788
        categorycode => $patron1->categorycode,
789
        itemtype => $item1->effective_itemtype,
790
        rules => {
791
            recalls_allowed => 1,
792
            recalls_per_record => 1,
793
            on_shelf_recalls => 'any',
794
        },
795
    });
796
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
797
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
798
799
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
800
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
801
802
    $recall1 = Koha::Recall->new({
803
        borrowernumber => $patron1->borrowernumber,
804
        recalldate => Koha::DateUtils::dt_from_string,
805
        biblionumber => $biblio->biblionumber,
806
        branchcode => $branchcode,
807
        status => 'R',
808
        itemnumber => undef,
809
        expirationdate => undef,
810
        item_level_recall => 0
811
    })->store;
812
813
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
814
815
    Koha::CirculationRules->set_rules({
816
        branchcode => undef,
817
        categorycode => undef,
818
        itemtype => $item1->effective_itemtype,
819
        rules => {
820
            recalls_allowed => 0,
821
            recalls_per_record => 1,
822
            on_shelf_recalls => 'any',
823
        },
824
    });
825
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
826
827
    Koha::CirculationRules->set_rules({
828
        branchcode => undef,
829
        categorycode => undef,
830
        itemtype => $item1->effective_itemtype,
831
        rules => {
832
            recalls_allowed => 1,
833
            recalls_per_record => 1,
834
            on_shelf_recalls => 'any',
835
        },
836
    });
837
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
838
839
    # check_recalls tests
840
841
    $recall1 = Koha::Recall->new({
842
        borrowernumber => $patron2->borrowernumber,
843
        recalldate => Koha::DateUtils::dt_from_string,
844
        biblionumber => $biblio->biblionumber,
845
        branchcode => $branchcode,
846
        status => 'R',
847
        itemnumber => $item1->itemnumber,
848
        expirationdate => undef,
849
        item_level_recall => 1
850
    })->store;
851
    $recall2 = Koha::Recall->new({
852
        borrowernumber => $patron1->borrowernumber,
853
        recalldate => Koha::DateUtils::dt_from_string,
854
        biblionumber => $biblio->biblionumber,
855
        branchcode => $branchcode,
856
        status => 'R',
857
        itemnumber => undef,
858
        expirationdate => undef,
859
        item_level_recall => 0
860
    })->store;
861
    $recall2->set_waiting({ item => $item1 });
862
863
    # return a waiting recall
864
    my $check_recall = $item1->check_recalls;
865
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
866
867
    $recall2->revert_waiting;
868
869
    # return recall based on recalldate
870
    $check_recall = $item1->check_recalls;
871
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
872
873
    $recall1->set_cancelled;
874
875
    # return a biblio-level recall
876
    $check_recall = $item1->check_recalls;
877
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
878
879
    $recall2->set_cancelled;
880
};
(-)a/t/db_dependent/Koha/Patron.t (+56 lines)
Lines 365-367 subtest 'is_superlibrarian() tests' => sub { Link Here
365
365
366
    $schema->storage->txn_rollback;
366
    $schema->storage->txn_rollback;
367
};
367
};
368
369
subtest 'recalls() tests' => sub {
370
371
    plan tests => 2;
372
373
    $schema->storage->txn_begin;
374
375
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
376
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
377
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
378
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
379
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
380
381
    Koha::Recall->new({
382
        biblionumber => $biblio1->biblionumber,
383
        borrowernumber => $patron->borrowernumber,
384
        itemnumber => $item1->itemnumber,
385
        branchcode => $patron->branchcode,
386
        recalldate => dt_from_string,
387
        status => 'R',
388
        item_level_recall => 1,
389
    })->store;
390
    Koha::Recall->new({
391
        biblionumber => $biblio2->biblionumber,
392
        borrowernumber => $patron->borrowernumber,
393
        itemnumber => $item2->itemnumber,
394
        branchcode => $patron->branchcode,
395
        recalldate => dt_from_string,
396
        status => 'R',
397
        item_level_recall => 1,
398
    })->store;
399
    Koha::Recall->new({
400
        biblionumber => $biblio1->biblionumber,
401
        borrowernumber => $patron->borrowernumber,
402
        itemnumber => undef,
403
        branchcode => $patron->branchcode,
404
        recalldate => dt_from_string,
405
        status => 'R',
406
        item_level_recall => 0,
407
    })->store;
408
    my $recall = Koha::Recall->new({
409
        biblionumber => $biblio1->biblionumber,
410
        borrowernumber => $patron->borrowernumber,
411
        itemnumber => undef,
412
        branchcode => $patron->branchcode,
413
        recalldate => dt_from_string,
414
        status => 'R',
415
        item_level_recall => 0,
416
    })->store;
417
    $recall->set_cancelled;
418
419
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
420
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
421
422
    $schema->storage->txn_rollback;
423
};
(-)a/t/db_dependent/Stats.t (-1 / +1 lines)
Lines 55-61 $return_error = $@; Link Here
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
56
56
57
# returns undef and croaks if mandatory params are missing
57
# returns undef and croaks if mandatory params are missing
58
my @allowed_circulation_types = qw (renew issue localuse return);
58
my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout recall);
59
my @allowed_accounts_types = qw (writeoff payment);
59
my @allowed_accounts_types = qw (writeoff payment);
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
(-)a/t/db_dependent/XSLT.t (-3 / +12 lines)
Lines 34-40 my $builder = t::lib::TestBuilder->new; Link Here
34
$schema->storage->txn_begin;
34
$schema->storage->txn_begin;
35
35
36
subtest 'buildKohaItemsNamespace status tests' => sub {
36
subtest 'buildKohaItemsNamespace status tests' => sub {
37
    plan tests => 13;
37
    plan tests => 14;
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
Lines 104-110 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
104
        }
104
        }
105
    });
105
    });
106
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
106
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
107
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit");
107
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (holds)");
108
    $hold->cancel;
108
109
109
    $builder->build({ source => "TmpHoldsqueue", value => {
110
    $builder->build({ source => "TmpHoldsqueue", value => {
110
        itemnumber => $item->itemnumber
111
        itemnumber => $item->itemnumber
Lines 113-118 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
113
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
114
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
114
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
115
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
115
116
117
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
118
        biblionumber    => $item->biblionumber,
119
        itemnumber      => $item->itemnumber,
120
        branchcode      => $item->holdingbranch,
121
        status          => 'R',
122
    }});
123
    $recall->set_waiting;
124
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
125
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
116
126
117
};
127
};
118
128
119
- 

Return to bug 19532