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

(-)a/C4/Circulation.pm (-2 / +120 lines)
Lines 299-304 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
299
299
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
301
301
302
=item C<RecallPlacedAtHoldingBranch>
303
304
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.
305
306
=item C<RecallFound>
307
308
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
309
302
=back
310
=back
303
311
304
=back
312
=back
Lines 372-377 sub transferbook { Link Here
372
        $dotransfer = 0 unless $ignoreRs;
380
        $dotransfer = 0 unless $ignoreRs;
373
    }
381
    }
374
382
383
    # find recall
384
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
385
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
386
        # do a transfer if the recall branch is different to the item holding branch
387
        if ( $recall->branchcode eq $fbr ) {
388
            $dotransfer = 0;
389
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
390
        } else {
391
            $dotransfer = 1;
392
            $messages->{'RecallFound'} = $recall;
393
        }
394
    }
395
375
    #actually do the transfer....
396
    #actually do the transfer....
376
    if ($dotransfer) {
397
    if ($dotransfer) {
377
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
398
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 722-727 sticky due date is invalid or due date in the past Link Here
722
743
723
if the borrower borrows to much things
744
if the borrower borrows to much things
724
745
746
=head3 RECALLED
747
748
recalled by someone else
749
725
=cut
750
=cut
726
751
727
sub CanBookBeIssued {
752
sub CanBookBeIssued {
Lines 1095-1101 sub CanBookBeIssued { Link Here
1095
        }
1120
        }
1096
    }
1121
    }
1097
1122
1098
    unless ( $ignore_reserves ) {
1123
    my $recall;
1124
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1125
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1126
    # Don't look at recalls that are in transit
1127
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1128
        my @recalls = $biblio->recalls;
1129
1130
        foreach my $r ( @recalls ) {
1131
            if ( $r->itemnumber and
1132
                $r->itemnumber == $item_object->itemnumber and
1133
                $r->borrowernumber == $patron->borrowernumber and
1134
                $r->waiting ) {
1135
                $messages{RECALLED} = $r->recall_id;
1136
                $recall = $r;
1137
                # this item is already waiting for this borrower and the recall can be fulfilled
1138
                last;
1139
            }
1140
            elsif ( $r->itemnumber and
1141
                $r->itemnumber == $item_object->itemnumber and
1142
                $r->in_transit ) {
1143
                # recalled item is in transit
1144
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1145
            }
1146
            elsif ( $r->item_level_recall and
1147
                $r->itemnumber == $item_object->itemnumber and
1148
                $r->borrowernumber != $patron->borrowernumber and
1149
                !$r->in_transit ) {
1150
                # this specific item has been recalled by a different patron
1151
                $needsconfirmation{RECALLED} = $r;
1152
                $recall = $r;
1153
                last;
1154
            }
1155
            elsif ( !$r->item_level_recall and
1156
                $r->borrowernumber != $patron->borrowernumber and
1157
                !$r->in_transit ) {
1158
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1159
                $needsconfirmation{RECALLED} = $r;
1160
                $recall = $r;
1161
                last;
1162
            }
1163
        }
1164
    }
1165
1166
    unless ( $ignore_reserves and defined $recall ) {
1099
        # See if the item is on reserve.
1167
        # See if the item is on reserve.
1100
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1168
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1101
        if ($restype) {
1169
        if ($restype) {
Lines 1410-1415 AddIssue does the following things : Link Here
1410
          * RESERVE PLACED ?
1478
          * RESERVE PLACED ?
1411
              - fill reserve if reserve to this patron
1479
              - fill reserve if reserve to this patron
1412
              - cancel reserve or not, otherwise
1480
              - cancel reserve or not, otherwise
1481
          * RECALL PLACED ?
1482
              - fill recall if recall to this patron
1483
              - cancel recall or not
1484
              - revert recall's waiting status or not
1413
          * TRANSFERT PENDING ?
1485
          * TRANSFERT PENDING ?
1414
              - complete the transfert
1486
              - complete the transfert
1415
          * ISSUE THE BOOK
1487
          * ISSUE THE BOOK
Lines 1424-1429 sub AddIssue { Link Here
1424
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1496
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1425
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1497
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1426
    my $auto_renew = $params && $params->{auto_renew};
1498
    my $auto_renew = $params && $params->{auto_renew};
1499
    my $cancel_recall = $params && $params->{cancel_recall};
1500
    my $recall_id = $params && $params->{recall_id};
1427
    my $dbh          = C4::Context->dbh;
1501
    my $dbh          = C4::Context->dbh;
1428
    my $barcodecheck = CheckValidBarcode($barcode);
1502
    my $barcodecheck = CheckValidBarcode($barcode);
1429
1503
Lines 1497-1502 sub AddIssue { Link Here
1497
                $item_object->discard_changes;
1571
                $item_object->discard_changes;
1498
            }
1572
            }
1499
1573
1574
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1575
1500
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1576
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1501
1577
1502
            # Starting process for transfer job (checking transfert and validate it if we have one)
1578
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1946-1951 Value 1 if return is successful. Link Here
1946
2022
1947
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2023
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1948
2024
2025
=item C<RecallFound>
2026
2027
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2028
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2029
branchcode.
2030
2031
=item C<TransferredRecall>
2032
2033
This item has been transferred to this branch to fill a recall. The recall object is returned.
2034
1949
=back
2035
=back
1950
2036
1951
C<$iteminformation> is a reference-to-hash, giving information about the
2037
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2217-2222 sub AddReturn { Link Here
2217
        }
2303
        }
2218
    }
2304
    }
2219
2305
2306
    # find recalls...
2307
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2308
    my $recall = undef;
2309
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2310
    if ( defined $recall ) {
2311
        $messages->{RecallFound} = $recall;
2312
        if ( $recall->branchcode ne $branch ) {
2313
            $messages->{RecallNeedsTransfer} = $branch;
2314
        }
2315
    }
2316
2220
    # find reserves.....
2317
    # find reserves.....
2221
    # launch the Checkreserves routine to find any holds
2318
    # launch the Checkreserves routine to find any holds
2222
    my ($resfound, $resrec);
2319
    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 && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2384
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
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 2800-2805 sub CanBookBeRenewed { Link Here
2800
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2906
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2801
    }
2907
    }
2802
2908
2909
    my $recall = undef;
2910
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2911
    if ( defined $recall ) {
2912
        if ( $recall->item_level_recall ) {
2913
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2914
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2915
        } else {
2916
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2917
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2918
        }
2919
    }
2920
2803
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2921
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2804
2922
2805
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2923
    # 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 1783-1788 sub searchResults { Link Here
1783
        my $item_in_transit_count = 0;
1783
        my $item_in_transit_count = 0;
1784
        my $item_onhold_count     = 0;
1784
        my $item_onhold_count     = 0;
1785
        my $notforloan_count      = 0;
1785
        my $notforloan_count      = 0;
1786
        my $item_recalled_count   = 0;
1786
        my $items_count           = scalar(@fields);
1787
        my $items_count           = scalar(@fields);
1787
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1788
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1788
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1789
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1874-1879 sub searchResults { Link Here
1874
                # is item on the reserve shelf?
1875
                # is item on the reserve shelf?
1875
                my $reservestatus = '';
1876
                my $reservestatus = '';
1876
1877
1878
                # is item a waiting recall?
1879
                my $recallstatus = '';
1880
1877
                unless ($item->{withdrawn}
1881
                unless ($item->{withdrawn}
1878
                        || $item->{itemlost}
1882
                        || $item->{itemlost}
1879
                        || $item->{damaged}
1883
                        || $item->{damaged}
Lines 1895-1900 sub searchResults { Link Here
1895
                    #
1899
                    #
1896
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1900
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1897
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1901
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1902
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
1898
                }
1903
                }
1899
1904
1900
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
1905
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 1903-1908 sub searchResults { Link Here
1903
                    || $item->{damaged}
1908
                    || $item->{damaged}
1904
                    || $item->{notforloan}
1909
                    || $item->{notforloan}
1905
                    || $reservestatus eq 'Waiting'
1910
                    || $reservestatus eq 'Waiting'
1911
                    || $recallstatus eq 'Waiting'
1906
                    || ($transfertwhen && $transfertwhen ne ''))
1912
                    || ($transfertwhen && $transfertwhen ne ''))
1907
                {
1913
                {
1908
                    $withdrawn_count++        if $item->{withdrawn};
1914
                    $withdrawn_count++        if $item->{withdrawn};
Lines 1910-1915 sub searchResults { Link Here
1910
                    $itemdamaged_count++     if $item->{damaged};
1916
                    $itemdamaged_count++     if $item->{damaged};
1911
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1917
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1912
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1918
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1919
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
1913
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1920
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1914
1921
1915
                    $other_count++;
1922
                    $other_count++;
Lines 1919-1924 sub searchResults { Link Here
1919
                        $other_items->{$key}->{$_} = $item->{$_};
1926
                        $other_items->{$key}->{$_} = $item->{$_};
1920
                    }
1927
                    }
1921
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1928
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1929
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
1922
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1930
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1923
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1931
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1924
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
1932
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2012-2017 sub searchResults { Link Here
2012
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2020
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2013
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2021
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2014
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2022
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2023
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2015
        $oldbiblio->{orderedcount}         = $ordered_count;
2024
        $oldbiblio->{orderedcount}         = $ordered_count;
2016
        $oldbiblio->{notforloancount}      = $notforloan_count;
2025
        $oldbiblio->{notforloancount}      = $notforloan_count;
2017
2026
(-)a/C4/XSLT.pm (-1 / +7 lines)
Lines 348-354 sub buildKohaItemsNamespace { Link Here
348
        my $status;
348
        my $status;
349
        my $substatus = '';
349
        my $substatus = '';
350
350
351
        if ($item->has_pending_hold) {
351
        my $recalls = Koha::Recalls->search({ itemnumber => $item->itemnumber, status => 'W' });
352
353
        if ( $recalls->count ) {
354
            # recalls take priority over holds
355
            $status = 'Waiting';
356
        }
357
        elsif ( $item->has_pending_hold ) {
352
            $status = 'other';
358
            $status = 'other';
353
            $substatus = 'Pending hold';
359
            $substatus = 'Pending hold';
354
        }
360
        }
(-)a/Koha/Biblio.pm (+108 lines)
Lines 942-947 sub get_marc_host { Link Here
942
    }
942
    }
943
}
943
}
944
944
945
=head3 recalls
946
947
    my @recalls = $biblio->recalls;
948
949
Return all active recalls attached to this biblio, sorted by oldest first
950
951
=cut
952
953
sub recalls {
954
    my ( $self ) = @_;
955
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
956
    return @recalls_rs;
957
}
958
959
=head3 can_be_recalled
960
961
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
962
963
Does biblio-level checks and returns the items attached to this biblio that are available for recall
964
965
=cut
966
967
sub can_be_recalled {
968
    my ( $self, $params ) = @_;
969
970
    return 0 if !( C4::Context->preference('UseRecalls') );
971
972
    my $patron = $params->{patron};
973
974
    my $branchcode = C4::Context->userenv->{'branch'};
975
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
976
        $branchcode = $patron->branchcode;
977
    }
978
979
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
980
981
    # if there are no available items at all, no recall can be placed
982
    return 0 if ( scalar @all_items == 0 );
983
984
    my @itemtypes;
985
    my @itemnumbers;
986
    my @items;
987
    foreach my $item ( @all_items ) {
988
        if ( $item->can_be_recalled({ patron => $patron }) ) {
989
            push( @itemtypes, $item->effective_itemtype );
990
            push( @itemnumbers, $item->itemnumber );
991
            push( @items, $item );
992
        }
993
    }
994
995
    # if there are no recallable items, no recall can be placed
996
    return 0 if ( scalar @items == 0 );
997
998
    # Check the circulation rule for each relevant itemtype for this biblio
999
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
1000
    foreach my $itemtype ( @itemtypes ) {
1001
        my $rule = Koha::CirculationRules->get_effective_rules({
1002
            branchcode => $branchcode,
1003
            categorycode => $patron ? $patron->categorycode : undef,
1004
            itemtype => $itemtype,
1005
            rules => [
1006
                'recalls_allowed',
1007
                'recalls_per_record',
1008
                'on_shelf_recalls',
1009
            ],
1010
        });
1011
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
1012
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
1013
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
1014
    }
1015
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
1016
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
1017
    my %on_shelf_recalls_count = ();
1018
    foreach my $count ( @on_shelf_recalls ) {
1019
        $on_shelf_recalls_count{$count}++;
1020
    }
1021
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
1022
1023
    # check recalls allowed has been set and is not zero
1024
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
1025
1026
    if ( $patron ) {
1027
        # check borrower has not reached open recalls allowed limit
1028
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
1029
1030
        # check borrower has not reached open recalls allowed per record limit
1031
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
1032
1033
        # check if any of the items under this biblio are already checked out by this borrower
1034
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
1035
    }
1036
1037
    # check item availability
1038
    my $checked_out_count = 0;
1039
    foreach (@items) {
1040
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1041
    }
1042
1043
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1044
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
1045
1046
    # can't recall if no items have been checked out
1047
    return 0 if ( $checked_out_count == 0 );
1048
1049
    # can recall
1050
    return @items;
1051
}
1052
945
=head2 Internal methods
1053
=head2 Internal methods
946
1054
947
=head3 type
1055
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1374-1379 sub _after_item_action_hooks { Link Here
1374
    );
1374
    );
1375
}
1375
}
1376
1376
1377
=head3 recall
1378
1379
    my $recall = $item->recall;
1380
1381
Return the relevant recall for this item
1382
1383
=cut
1384
1385
sub recall {
1386
    my ( $self ) = @_;
1387
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1388
    foreach my $recall (@recalls) {
1389
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1390
            return $recall;
1391
        }
1392
    }
1393
    # no item-level recall to return, so return earliest biblio-level
1394
    # FIXME: eventually this will be based on priority
1395
    return $recalls[0];
1396
}
1397
1398
=head3 can_be_recalled
1399
1400
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1401
1402
Does item-level checks and returns if items can be recalled by this borrower
1403
1404
=cut
1405
1406
sub can_be_recalled {
1407
    my ( $self, $params ) = @_;
1408
1409
    return 0 if !( C4::Context->preference('UseRecalls') );
1410
1411
    # check if this item is not for loan, withdrawn or lost
1412
    return 0 if ( $self->notforloan != 0 );
1413
    return 0 if ( $self->itemlost != 0 );
1414
    return 0 if ( $self->withdrawn != 0 );
1415
1416
    # check if this item is not checked out - if not checked out, can't be recalled
1417
    return 0 if ( !defined( $self->checkout ) );
1418
1419
    my $patron = $params->{patron};
1420
1421
    my $branchcode = C4::Context->userenv->{'branch'};
1422
    if ( $patron ) {
1423
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1424
    }
1425
1426
    # Check the circulation rule for each relevant itemtype for this item
1427
    my $rule = Koha::CirculationRules->get_effective_rules({
1428
        branchcode => $branchcode,
1429
        categorycode => $patron ? $patron->categorycode : undef,
1430
        itemtype => $self->effective_itemtype,
1431
        rules => [
1432
            'recalls_allowed',
1433
            'recalls_per_record',
1434
            'on_shelf_recalls',
1435
        ],
1436
    });
1437
1438
    # check recalls allowed has been set and is not zero
1439
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1440
1441
    if ( $patron ) {
1442
        # check borrower has not reached open recalls allowed limit
1443
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1444
1445
        # check borrower has not reach open recalls allowed per record limit
1446
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1447
1448
        # check if this patron has already recalled this item
1449
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1450
1451
        # check if this patron has already checked out this item
1452
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1453
1454
        # check if this patron has already reserved this item
1455
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1456
    }
1457
1458
    # check item availability
1459
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1460
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1461
1462
    # if there are no available items at all, no recall can be placed
1463
    return 0 if ( scalar @items == 0 );
1464
1465
    my $checked_out_count = 0;
1466
    foreach (@items) {
1467
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1468
    }
1469
1470
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1471
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1472
1473
    # can't recall if no items have been checked out
1474
    return 0 if ( $checked_out_count == 0 );
1475
1476
    # can recall
1477
    return 1;
1478
}
1479
1480
=head3 can_be_waiting_recall
1481
1482
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1483
1484
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1485
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1486
1487
=cut
1488
1489
sub can_be_waiting_recall {
1490
    my ( $self ) = @_;
1491
1492
    return 0 if !( C4::Context->preference('UseRecalls') );
1493
1494
    # check if this item is not for loan, withdrawn or lost
1495
    return 0 if ( $self->notforloan != 0 );
1496
    return 0 if ( $self->itemlost != 0 );
1497
    return 0 if ( $self->withdrawn != 0 );
1498
1499
    my $branchcode = $self->holdingbranch;
1500
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1501
        $branchcode = C4::Context->userenv->{'branch'};
1502
    } else {
1503
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1504
    }
1505
1506
    # Check the circulation rule for each relevant itemtype for this item
1507
    my $rule = Koha::CirculationRules->get_effective_rules({
1508
        branchcode => $branchcode,
1509
        categorycode => undef,
1510
        itemtype => $self->effective_itemtype,
1511
        rules => [
1512
            'recalls_allowed',
1513
        ],
1514
    });
1515
1516
    # check recalls allowed has been set and is not zero
1517
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1518
1519
    # can recall
1520
    return 1;
1521
}
1522
1523
=head3 check_recalls
1524
1525
    my $recall = $item->check_recalls;
1526
1527
Get the most relevant recall for this item.
1528
1529
=cut
1530
1531
sub check_recalls {
1532
    my ( $self ) = @_;
1533
1534
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1535
1536
    my $recall;
1537
    # iterate through relevant recalls to find the best one.
1538
    # if we come across a waiting recall, use this one.
1539
    # 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.
1540
    foreach my $r ( @recalls ) {
1541
        if ( $r->waiting ) {
1542
            $recall = $r;
1543
            last;
1544
        }
1545
    }
1546
    unless ( defined $recall ) {
1547
        $recall = $recalls[0];
1548
    }
1549
1550
    return $recall;
1551
}
1552
1377
=head3 _type
1553
=head3 _type
1378
1554
1379
=cut
1555
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1908-1913 sub queue_notice { Link Here
1908
    return \%return;
1908
    return \%return;
1909
}
1909
}
1910
1910
1911
=head3 recalls
1912
1913
    my $recalls = $patron->recalls;
1914
1915
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1916
1917
Return the patron's active recalls - total, or on a specific biblio
1918
1919
=cut
1920
1921
sub recalls {
1922
    my ( $self, $params ) = @_;
1923
1924
    my $biblionumber = $params->{biblionumber};
1925
1926
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1927
1928
    if ( $biblionumber ) {
1929
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1930
    }
1931
1932
    return $recalls_rs;
1933
}
1934
1911
=head2 Internal methods
1935
=head2 Internal methods
1912
1936
1913
=head3 _type
1937
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 26-31 use Koha::Holds; Link Here
26
use Koha::Biblios;
26
use Koha::Biblios;
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::Recalls;
29
30
30
sub HoldsCount {
31
sub HoldsCount {
31
    my ( $self, $biblionumber ) = @_;
32
    my ( $self, $biblionumber ) = @_;
Lines 56-59 sub CanArticleRequest { Link Here
56
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
}
58
}
58
59
60
sub RecallsCount {
61
    my ( $self, $biblionumber ) = @_;
62
63
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
64
65
    return $recalls->count;
66
}
67
59
1;
68
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 => 56;
21
use Test::More tests => 59;
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 417-423 subtest "GetIssuingCharges tests" => sub { Link Here
417
417
418
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
418
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
419
subtest "CanBookBeRenewed tests" => sub {
419
subtest "CanBookBeRenewed tests" => sub {
420
    plan tests => 95;
420
    plan tests => 99;
421
421
422
    C4::Context->set_preference('ItemsDeniedRenewal','');
422
    C4::Context->set_preference('ItemsDeniedRenewal','');
423
    # Generate test biblio
423
    # Generate test biblio
Lines 1446-1451 subtest "CanBookBeRenewed tests" => sub { Link Here
1446
            $item_3->itemcallnumber || '' ),
1446
            $item_3->itemcallnumber || '' ),
1447
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1447
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1448
    );
1448
    );
1449
1450
    # Recalls
1451
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1452
    Koha::CirculationRules->set_rules({
1453
        categorycode => undef,
1454
        branchcode => undef,
1455
        itemtype => undef,
1456
        rules => {
1457
            recalls_allowed => 10,
1458
            renewalsallowed => 5,
1459
        },
1460
    });
1461
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1462
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1463
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1464
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1465
1466
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1467
1468
    # item-level and this item: renewal not allowed
1469
    my $recall = Koha::Recall->new({
1470
        biblionumber => $recall_item1->biblionumber,
1471
        itemnumber => $recall_item1->itemnumber,
1472
        borrowernumber => $recall_borrower->borrowernumber,
1473
        branchcode => $recall_borrower->branchcode,
1474
        item_level_recall => 1,
1475
        status => 'R',
1476
    })->store;
1477
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1478
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1479
    $recall->set_cancelled;
1480
1481
    # biblio-level requested recall: renewal not allowed
1482
    $recall = Koha::Recall->new({
1483
        biblionumber => $recall_item1->biblionumber,
1484
        itemnumber => undef,
1485
        borrowernumber => $recall_borrower->borrowernumber,
1486
        branchcode => $recall_borrower->branchcode,
1487
        item_level_recall => 0,
1488
        status => 'R',
1489
    })->store;
1490
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1491
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1492
    $recall->set_cancelled;
1493
1494
    # item-level and not this item: renewal allowed
1495
    $recall = Koha::Recall->new({
1496
        biblionumber => $recall_item2->biblionumber,
1497
        itemnumber => $recall_item2->itemnumber,
1498
        borrowernumber => $recall_borrower->borrowernumber,
1499
        branchcode => $recall_borrower->branchcode,
1500
        item_level_recall => 1,
1501
        status => 'R',
1502
    })->store;
1503
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1504
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1505
    $recall->set_cancelled;
1506
1507
    # biblio-level waiting recall: renewal allowed
1508
    $recall = Koha::Recall->new({
1509
        biblionumber => $recall_item1->biblionumber,
1510
        itemnumber => undef,
1511
        borrowernumber => $recall_borrower->borrowernumber,
1512
        branchcode => $recall_borrower->branchcode,
1513
        item_level_recall => 0,
1514
        status => 'R',
1515
    })->store;
1516
    $recall->set_waiting({ item => $recall_item1 });
1517
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1518
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1519
    $recall->set_cancelled;
1449
};
1520
};
1450
1521
1451
subtest "GetUpcomingDueIssues" => sub {
1522
subtest "GetUpcomingDueIssues" => sub {
Lines 1926-1931 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1926
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1997
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1927
};
1998
};
1928
1999
2000
subtest 'AddIssue | recalls' => sub {
2001
    plan tests => 3;
2002
2003
    t::lib::Mocks::mock_preference("UseRecalls", 1);
2004
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
2005
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
2006
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
2007
    my $item = $builder->build_sample_item;
2008
    Koha::CirculationRules->set_rules({
2009
        branchcode => undef,
2010
        itemtype => undef,
2011
        categorycode => undef,
2012
        rules => {
2013
            recalls_allowed => 10,
2014
        },
2015
    });
2016
2017
    # checking out item that they have recalled
2018
    my $recall1 = Koha::Recall->new({
2019
        borrowernumber => $patron1->borrowernumber,
2020
        biblionumber => $item->biblionumber,
2021
        itemnumber => $item->itemnumber,
2022
        item_level_recall => 1,
2023
        branchcode => $patron1->branchcode,
2024
        status => 'R',
2025
    })->store;
2026
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
2027
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
2028
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
2029
    AddReturn( $item->barcode, $item->homebranch );
2030
2031
    # this item is has a recall request. cancel recall
2032
    my $recall2 = Koha::Recall->new({
2033
        borrowernumber => $patron2->borrowernumber,
2034
        biblionumber => $item->biblionumber,
2035
        itemnumber => $item->itemnumber,
2036
        item_level_recall => 1,
2037
        branchcode => $patron2->branchcode,
2038
        status => 'R',
2039
    })->store;
2040
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
2041
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
2042
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
2043
    AddReturn( $item->barcode, $item->homebranch );
2044
2045
    # this item is waiting to fulfill a recall. revert recall
2046
    my $recall3 = Koha::Recall->new({
2047
        borrowernumber => $patron2->borrowernumber,
2048
        biblionumber => $item->biblionumber,
2049
        itemnumber => $item->itemnumber,
2050
        item_level_recall => 1,
2051
        branchcode => $patron2->branchcode,
2052
        status => 'R',
2053
    })->store;
2054
    $recall3->set_waiting;
2055
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
2056
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
2057
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
2058
    AddReturn( $item->barcode, $item->homebranch );
2059
};
2060
2061
1929
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
2062
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1930
    plan tests => 8;
2063
    plan tests => 8;
1931
2064
Lines 3844-3849 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3844
    # TODO test with AllowNotForLoanOverride = 1
3977
    # TODO test with AllowNotForLoanOverride = 1
3845
};
3978
};
3846
3979
3980
subtest 'CanBookBeIssued | recalls' => sub {
3981
    plan tests => 3;
3982
3983
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3984
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3985
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3986
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3987
    my $item = $builder->build_sample_item;
3988
    Koha::CirculationRules->set_rules({
3989
        branchcode => undef,
3990
        itemtype => undef,
3991
        categorycode => undef,
3992
        rules => {
3993
            recalls_allowed => 10,
3994
        },
3995
    });
3996
3997
    # item-level recall
3998
    my $recall = Koha::Recall->new({
3999
        borrowernumber => $patron1->borrowernumber,
4000
        biblionumber => $item->biblionumber,
4001
        itemnumber => $item->itemnumber,
4002
        item_level_recall => 1,
4003
        branchcode => $patron1->branchcode,
4004
        status => 'R',
4005
    })->store;
4006
4007
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4008
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
4009
4010
    $recall->set_cancelled;
4011
4012
    # biblio-level recall
4013
    $recall = Koha::Recall->new({
4014
        borrowernumber => $patron1->borrowernumber,
4015
        biblionumber => $item->biblionumber,
4016
        itemnumber => undef,
4017
        item_level_recall => 0,
4018
        branchcode => $patron1->branchcode,
4019
        status => 'R',
4020
    })->store;
4021
4022
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4023
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
4024
4025
    $recall->set_cancelled;
4026
4027
    # biblio-level recall
4028
    $recall = Koha::Recall->new({
4029
        borrowernumber => $patron1->borrowernumber,
4030
        biblionumber => $item->biblionumber,
4031
        itemnumber => undef,
4032
        item_level_recall => 0,
4033
        branchcode => $patron1->branchcode,
4034
        status => 'R',
4035
    })->store;
4036
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
4037
4038
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
4039
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
4040
4041
    $recall->set_cancelled;
4042
};
4043
3847
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
4044
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3848
    plan tests => 1;
4045
    plan tests => 1;
3849
4046
Lines 3859-3864 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3859
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
4056
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3860
};
4057
};
3861
4058
4059
subtest 'AddReturn | recalls' => sub {
4060
    plan tests => 3;
4061
4062
    t::lib::Mocks::mock_preference("UseRecalls", 1);
4063
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
4064
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
4065
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
4066
    my $item1 = $builder->build_sample_item;
4067
    Koha::CirculationRules->set_rules({
4068
        branchcode => undef,
4069
        itemtype => undef,
4070
        categorycode => undef,
4071
        rules => {
4072
            recalls_allowed => 10,
4073
        },
4074
    });
4075
4076
    # this item can fill a recall with pickup at this branch
4077
    AddIssue( $patron1->unblessed, $item1->barcode );
4078
    my $recall1 = Koha::Recall->new({
4079
        borrowernumber => $patron2->borrowernumber,
4080
        biblionumber => $item1->biblionumber,
4081
        itemnumber => $item1->itemnumber,
4082
        item_level_recall => 1,
4083
        branchcode => $item1->homebranch,
4084
        status => 'R',
4085
    })->store;
4086
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4087
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
4088
    $recall1->set_cancelled;
4089
4090
    # this item can fill a recall but needs transfer
4091
    AddIssue( $patron1->unblessed, $item1->barcode );
4092
    $recall1 = Koha::Recall->new({
4093
        borrowernumber => $patron2->borrowernumber,
4094
        biblionumber => $item1->biblionumber,
4095
        itemnumber => $item1->itemnumber,
4096
        item_level_recall => 1,
4097
        branchcode => $patron2->branchcode,
4098
        status => 'R',
4099
    })->store;
4100
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4101
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
4102
    $recall1->set_cancelled;
4103
4104
    # this item is already in transit, do not ask to transfer
4105
    AddIssue( $patron1->unblessed, $item1->barcode );
4106
    $recall1 = Koha::Recall->new({
4107
        borrowernumber => $patron2->borrowernumber,
4108
        biblionumber => $item1->biblionumber,
4109
        itemnumber => $item1->itemnumber,
4110
        item_level_recall => 1,
4111
        branchcode => $patron2->branchcode,
4112
        status => 'R',
4113
    })->store;
4114
    $recall1->start_transfer;
4115
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
4116
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
4117
    $recall1->set_cancelled;
4118
};
3862
4119
3863
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
4120
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3864
4121
(-)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 => 16;
20
use Test::More tests => 17;
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 736-738 subtest 'article_requests() tests' => sub { Link Here
736
736
737
    $schema->storage->txn_rollback;
737
    $schema->storage->txn_rollback;
738
};
738
};
739
740
subtest 'Recalls tests' => sub {
741
742
    plan tests => 12;
743
744
    $schema->storage->txn_begin;
745
    my $item1 = $builder->build_sample_item;
746
    my $biblio = $item1->biblio;
747
    my $branchcode = $item1->holdingbranch;
748
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
749
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
750
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
751
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
752
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
753
754
    my $recall1 = Koha::Recall->new({
755
        borrowernumber => $patron1->borrowernumber,
756
        recalldate => Koha::DateUtils::dt_from_string,
757
        biblionumber => $biblio->biblionumber,
758
        branchcode => $branchcode,
759
        status => 'R',
760
        itemnumber => $item1->itemnumber,
761
        expirationdate => undef,
762
        item_level_recall => 1
763
    })->store;
764
    my $recall2 = Koha::Recall->new({
765
        borrowernumber => $patron2->borrowernumber,
766
        recalldate => Koha::DateUtils::dt_from_string,
767
        biblionumber => $biblio->biblionumber,
768
        branchcode => $branchcode,
769
        status => 'R',
770
        itemnumber => undef,
771
        expirationdate => undef,
772
        item_level_recall => 0
773
    })->store;
774
    my $recall3 = Koha::Recall->new({
775
        borrowernumber => $patron3->borrowernumber,
776
        recalldate => Koha::DateUtils::dt_from_string,
777
        biblionumber => $biblio->biblionumber,
778
        branchcode => $branchcode,
779
        status => 'R',
780
        itemnumber => $item1->itemnumber,
781
        expirationdate => undef,
782
        item_level_recall => 1
783
    })->store;
784
785
    my $recalls_count = scalar $biblio->recalls;
786
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
787
788
    $recall1->set_cancelled;
789
    $recall2->set_expired({ interface => 'COMMANDLINE' });
790
791
    $recalls_count = scalar $biblio->recalls;
792
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
793
794
    t::lib::Mocks::mock_preference('UseRecalls', 0);
795
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
796
797
    t::lib::Mocks::mock_preference("UseRecalls", 1);
798
    $item1->update({ notforloan => 1 });
799
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
800
801
    $item1->update({ notforloan => 0 });
802
    Koha::CirculationRules->set_rules({
803
        branchcode => $branchcode,
804
        categorycode => $patron1->categorycode,
805
        itemtype => $item1->effective_itemtype,
806
        rules => {
807
            recalls_allowed => 0,
808
            recalls_per_record => 1,
809
            on_shelf_recalls => 'all',
810
        },
811
    });
812
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
813
814
    Koha::CirculationRules->set_rules({
815
        branchcode => $branchcode,
816
        categorycode => $patron1->categorycode,
817
        itemtype => $item1->effective_itemtype,
818
        rules => {
819
            recalls_allowed => 1,
820
            recalls_per_record => 1,
821
            on_shelf_recalls => 'all',
822
        },
823
    });
824
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
825
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
826
827
    $recall1->set_cancelled;
828
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
829
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
830
831
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
832
833
    Koha::CirculationRules->set_rules({
834
        branchcode => $branchcode,
835
        categorycode => $patron1->categorycode,
836
        itemtype => $item1->effective_itemtype,
837
        rules => {
838
            recalls_allowed => 1,
839
            recalls_per_record => 1,
840
            on_shelf_recalls => 'any',
841
        },
842
    });
843
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
844
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
845
846
    $recall2->set_cancelled;
847
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
848
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
849
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
850
851
    $item1->update({ withdrawn => 1 });
852
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
853
854
    $schema->storage->txn_rollback;
855
};
(-)a/t/db_dependent/Koha/Item.t (-1 / +187 lines)
Lines 20-26 Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
use utf8;
21
use utf8;
22
22
23
use Test::More tests => 13;
23
use Test::More tests => 14;
24
use Test::Exception;
24
use Test::Exception;
25
25
26
use C4::Biblio qw( GetMarcSubfieldStructure );
26
use C4::Biblio qw( GetMarcSubfieldStructure );
Lines 1146-1150 subtest 'columns_to_str' => sub { Link Here
1146
    $cache->clear_from_cache("MarcSubfieldStructure-");
1146
    $cache->clear_from_cache("MarcSubfieldStructure-");
1147
1147
1148
    $schema->storage->txn_rollback;
1148
    $schema->storage->txn_rollback;
1149
};
1150
1151
subtest 'Recalls tests' => sub {
1152
1153
    plan tests => 20;
1154
1155
    $schema->storage->txn_begin;
1156
1157
    my $item1 = $builder->build_sample_item;
1158
    my $biblio = $item1->biblio;
1159
    my $branchcode = $item1->holdingbranch;
1160
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1161
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1162
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1163
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
1164
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
1165
1166
    my $recall1 = Koha::Recall->new({
1167
        borrowernumber => $patron1->borrowernumber,
1168
        recalldate => Koha::DateUtils::dt_from_string,
1169
        biblionumber => $biblio->biblionumber,
1170
        branchcode => $branchcode,
1171
        status => 'R',
1172
        itemnumber => $item1->itemnumber,
1173
        expirationdate => undef,
1174
        item_level_recall => 1
1175
    })->store;
1176
    my $recall2 = Koha::Recall->new({
1177
        borrowernumber => $patron2->borrowernumber,
1178
        recalldate => Koha::DateUtils::dt_from_string,
1179
        biblionumber => $biblio->biblionumber,
1180
        branchcode => $branchcode,
1181
        status => 'R',
1182
        itemnumber => $item1->itemnumber,
1183
        expirationdate => undef,
1184
        item_level_recall =>1
1185
    })->store;
1186
1187
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
1188
1189
    $recall2->set_cancelled;
1190
1191
    t::lib::Mocks::mock_preference('UseRecalls', 0);
1192
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
1193
1194
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1195
1196
    $item1->update({ notforloan => 1 });
1197
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
1198
    $item1->update({ notforloan => 0, itemlost => 1 });
1199
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
1200
    $item1->update({ itemlost => 0, withdrawn => 1 });
1201
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
1202
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
1203
1204
    $item1->update({ withdrawn => 0 });
1205
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1206
1207
    Koha::CirculationRules->set_rules({
1208
        branchcode => $branchcode,
1209
        categorycode => $patron1->categorycode,
1210
        itemtype => $item1->effective_itemtype,
1211
        rules => {
1212
            recalls_allowed => 0,
1213
            recalls_per_record => 1,
1214
            on_shelf_recalls => 'all',
1215
        },
1216
    });
1217
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
1218
1219
    Koha::CirculationRules->set_rules({
1220
        branchcode => $branchcode,
1221
        categorycode => $patron1->categorycode,
1222
        itemtype => $item1->effective_itemtype,
1223
        rules => {
1224
            recalls_allowed => 1,
1225
            recalls_per_record => 1,
1226
            on_shelf_recalls => 'all',
1227
        },
1228
    });
1229
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
1230
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
1231
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
1232
1233
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
1234
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
1235
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
1236
1237
    $recall1->set_cancelled;
1238
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
1239
1240
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
1241
1242
    Koha::CirculationRules->set_rules({
1243
        branchcode => $branchcode,
1244
        categorycode => $patron1->categorycode,
1245
        itemtype => $item1->effective_itemtype,
1246
        rules => {
1247
            recalls_allowed => 1,
1248
            recalls_per_record => 1,
1249
            on_shelf_recalls => 'any',
1250
        },
1251
    });
1252
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
1253
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
1254
1255
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1256
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
1257
1258
    $recall1 = Koha::Recall->new({
1259
        borrowernumber => $patron1->borrowernumber,
1260
        recalldate => Koha::DateUtils::dt_from_string,
1261
        biblionumber => $biblio->biblionumber,
1262
        branchcode => $branchcode,
1263
        status => 'R',
1264
        itemnumber => undef,
1265
        expirationdate => undef,
1266
        item_level_recall => 0
1267
    })->store;
1268
1269
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
1270
1271
    Koha::CirculationRules->set_rules({
1272
        branchcode => undef,
1273
        categorycode => undef,
1274
        itemtype => $item1->effective_itemtype,
1275
        rules => {
1276
            recalls_allowed => 0,
1277
            recalls_per_record => 1,
1278
            on_shelf_recalls => 'any',
1279
        },
1280
    });
1281
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
1282
1283
    Koha::CirculationRules->set_rules({
1284
        branchcode => undef,
1285
        categorycode => undef,
1286
        itemtype => $item1->effective_itemtype,
1287
        rules => {
1288
            recalls_allowed => 1,
1289
            recalls_per_record => 1,
1290
            on_shelf_recalls => 'any',
1291
        },
1292
    });
1293
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
1294
1295
    # check_recalls tests
1296
1297
    $recall1 = Koha::Recall->new({
1298
        borrowernumber => $patron2->borrowernumber,
1299
        recalldate => Koha::DateUtils::dt_from_string,
1300
        biblionumber => $biblio->biblionumber,
1301
        branchcode => $branchcode,
1302
        status => 'R',
1303
        itemnumber => $item1->itemnumber,
1304
        expirationdate => undef,
1305
        item_level_recall => 1
1306
    })->store;
1307
    $recall2 = Koha::Recall->new({
1308
        borrowernumber => $patron1->borrowernumber,
1309
        recalldate => Koha::DateUtils::dt_from_string,
1310
        biblionumber => $biblio->biblionumber,
1311
        branchcode => $branchcode,
1312
        status => 'R',
1313
        itemnumber => undef,
1314
        expirationdate => undef,
1315
        item_level_recall => 0
1316
    })->store;
1317
    $recall2->set_waiting({ item => $item1 });
1318
1319
    # return a waiting recall
1320
    my $check_recall = $item1->check_recalls;
1321
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
1322
1323
    $recall2->revert_waiting;
1324
1325
    # return recall based on recalldate
1326
    $check_recall = $item1->check_recalls;
1327
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
1328
1329
    $recall1->set_cancelled;
1330
1331
    # return a biblio-level recall
1332
    $check_recall = $item1->check_recalls;
1333
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
1149
1334
1335
    $recall2->set_cancelled;
1150
};
1336
};
(-)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 => 10;
22
use Test::More tests => 11;
23
use Test::Exception;
23
use Test::Exception;
24
use Test::Warn;
24
use Test::Warn;
25
25
Lines 846-848 subtest 'article_requests() tests' => sub { Link Here
846
846
847
    $schema->storage->txn_rollback;
847
    $schema->storage->txn_rollback;
848
};
848
};
849
850
subtest 'recalls() tests' => sub {
851
852
    plan tests => 2;
853
854
    $schema->storage->txn_begin;
855
856
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
857
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
858
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
859
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
860
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
861
862
    Koha::Recall->new({
863
        biblionumber => $biblio1->biblionumber,
864
        borrowernumber => $patron->borrowernumber,
865
        itemnumber => $item1->itemnumber,
866
        branchcode => $patron->branchcode,
867
        recalldate => dt_from_string,
868
        status => 'R',
869
        item_level_recall => 1,
870
    })->store;
871
    Koha::Recall->new({
872
        biblionumber => $biblio2->biblionumber,
873
        borrowernumber => $patron->borrowernumber,
874
        itemnumber => $item2->itemnumber,
875
        branchcode => $patron->branchcode,
876
        recalldate => dt_from_string,
877
        status => 'R',
878
        item_level_recall => 1,
879
    })->store;
880
    Koha::Recall->new({
881
        biblionumber => $biblio1->biblionumber,
882
        borrowernumber => $patron->borrowernumber,
883
        itemnumber => undef,
884
        branchcode => $patron->branchcode,
885
        recalldate => dt_from_string,
886
        status => 'R',
887
        item_level_recall => 0,
888
    })->store;
889
    my $recall = Koha::Recall->new({
890
        biblionumber => $biblio1->biblionumber,
891
        borrowernumber => $patron->borrowernumber,
892
        itemnumber => undef,
893
        branchcode => $patron->branchcode,
894
        recalldate => dt_from_string,
895
        status => 'R',
896
        item_level_recall => 0,
897
    })->store;
898
    $recall->set_cancelled;
899
900
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
901
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
902
903
    $schema->storage->txn_rollback;
904
};
(-)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 / +14 lines)
Lines 48-54 subtest 'transformMARCXML4XSLT tests' => sub { Link Here
48
};
48
};
49
49
50
subtest 'buildKohaItemsNamespace status tests' => sub {
50
subtest 'buildKohaItemsNamespace status tests' => sub {
51
    plan tests => 16;
51
    plan tests => 17;
52
52
53
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
53
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
54
    t::lib::Mocks::mock_preference( 'OPACResultsLibrary', 'holdingbranch' );
54
    t::lib::Mocks::mock_preference( 'OPACResultsLibrary', 'holdingbranch' );
Lines 131-137 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
131
        }
131
        }
132
    });
132
    });
133
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
133
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
134
    like($xml,qr{<substatus>Waiting</substatus>},"Waiting status takes precedence over In transit");
134
    like($xml,qr{<substatus>Waiting</substatus>},"Waiting status takes precedence over In transit (holds)");
135
    $hold->cancel;
135
136
136
    $builder->build({ source => "TmpHoldsqueue", value => {
137
    $builder->build({ source => "TmpHoldsqueue", value => {
137
        itemnumber => $item->itemnumber
138
        itemnumber => $item->itemnumber
Lines 141-146 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
141
    like($xml,qr{<substatus>Pending hold</substatus>},"Pending status takes precedence over all");
142
    like($xml,qr{<substatus>Pending hold</substatus>},"Pending status takes precedence over all");
142
    my $library_name = $holdinglibrary->branchname;
143
    my $library_name = $holdinglibrary->branchname;
143
    like($xml,qr{<resultbranch>${library_name}</resultbranch>}, "Found resultbranch / holding branch" );
144
    like($xml,qr{<resultbranch>${library_name}</resultbranch>}, "Found resultbranch / holding branch" );
145
146
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
147
        biblionumber    => $item->biblionumber,
148
        itemnumber      => $item->itemnumber,
149
        branchcode      => $item->holdingbranch,
150
        status          => 'R',
151
    }});
152
    $recall->set_waiting;
153
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
154
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
155
144
};
156
};
145
157
146
$schema->storage->txn_rollback;
158
$schema->storage->txn_rollback;
147
- 

Return to bug 19532