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 1410-1415 sub _after_item_action_hooks { Link Here
1410
    );
1410
    );
1411
}
1411
}
1412
1412
1413
=head3 recall
1414
1415
    my $recall = $item->recall;
1416
1417
Return the relevant recall for this item
1418
1419
=cut
1420
1421
sub recall {
1422
    my ( $self ) = @_;
1423
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1424
    foreach my $recall (@recalls) {
1425
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1426
            return $recall;
1427
        }
1428
    }
1429
    # no item-level recall to return, so return earliest biblio-level
1430
    # FIXME: eventually this will be based on priority
1431
    return $recalls[0];
1432
}
1433
1434
=head3 can_be_recalled
1435
1436
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1437
1438
Does item-level checks and returns if items can be recalled by this borrower
1439
1440
=cut
1441
1442
sub can_be_recalled {
1443
    my ( $self, $params ) = @_;
1444
1445
    return 0 if !( C4::Context->preference('UseRecalls') );
1446
1447
    # check if this item is not for loan, withdrawn or lost
1448
    return 0 if ( $self->notforloan != 0 );
1449
    return 0 if ( $self->itemlost != 0 );
1450
    return 0 if ( $self->withdrawn != 0 );
1451
1452
    # check if this item is not checked out - if not checked out, can't be recalled
1453
    return 0 if ( !defined( $self->checkout ) );
1454
1455
    my $patron = $params->{patron};
1456
1457
    my $branchcode = C4::Context->userenv->{'branch'};
1458
    if ( $patron ) {
1459
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1460
    }
1461
1462
    # Check the circulation rule for each relevant itemtype for this item
1463
    my $rule = Koha::CirculationRules->get_effective_rules({
1464
        branchcode => $branchcode,
1465
        categorycode => $patron ? $patron->categorycode : undef,
1466
        itemtype => $self->effective_itemtype,
1467
        rules => [
1468
            'recalls_allowed',
1469
            'recalls_per_record',
1470
            'on_shelf_recalls',
1471
        ],
1472
    });
1473
1474
    # check recalls allowed has been set and is not zero
1475
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1476
1477
    if ( $patron ) {
1478
        # check borrower has not reached open recalls allowed limit
1479
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1480
1481
        # check borrower has not reach open recalls allowed per record limit
1482
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1483
1484
        # check if this patron has already recalled this item
1485
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1486
1487
        # check if this patron has already checked out this item
1488
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1489
1490
        # check if this patron has already reserved this item
1491
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1492
    }
1493
1494
    # check item availability
1495
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1496
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1497
1498
    # if there are no available items at all, no recall can be placed
1499
    return 0 if ( scalar @items == 0 );
1500
1501
    my $checked_out_count = 0;
1502
    foreach (@items) {
1503
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1504
    }
1505
1506
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1507
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1508
1509
    # can't recall if no items have been checked out
1510
    return 0 if ( $checked_out_count == 0 );
1511
1512
    # can recall
1513
    return 1;
1514
}
1515
1516
=head3 can_be_waiting_recall
1517
1518
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1519
1520
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1521
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1522
1523
=cut
1524
1525
sub can_be_waiting_recall {
1526
    my ( $self ) = @_;
1527
1528
    return 0 if !( C4::Context->preference('UseRecalls') );
1529
1530
    # check if this item is not for loan, withdrawn or lost
1531
    return 0 if ( $self->notforloan != 0 );
1532
    return 0 if ( $self->itemlost != 0 );
1533
    return 0 if ( $self->withdrawn != 0 );
1534
1535
    my $branchcode = $self->holdingbranch;
1536
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1537
        $branchcode = C4::Context->userenv->{'branch'};
1538
    } else {
1539
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1540
    }
1541
1542
    # Check the circulation rule for each relevant itemtype for this item
1543
    my $rule = Koha::CirculationRules->get_effective_rules({
1544
        branchcode => $branchcode,
1545
        categorycode => undef,
1546
        itemtype => $self->effective_itemtype,
1547
        rules => [
1548
            'recalls_allowed',
1549
        ],
1550
    });
1551
1552
    # check recalls allowed has been set and is not zero
1553
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1554
1555
    # can recall
1556
    return 1;
1557
}
1558
1559
=head3 check_recalls
1560
1561
    my $recall = $item->check_recalls;
1562
1563
Get the most relevant recall for this item.
1564
1565
=cut
1566
1567
sub check_recalls {
1568
    my ( $self ) = @_;
1569
1570
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1571
1572
    my $recall;
1573
    # iterate through relevant recalls to find the best one.
1574
    # if we come across a waiting recall, use this one.
1575
    # 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.
1576
    foreach my $r ( @recalls ) {
1577
        if ( $r->waiting ) {
1578
            $recall = $r;
1579
            last;
1580
        }
1581
    }
1582
    unless ( defined $recall ) {
1583
        $recall = $recalls[0];
1584
    }
1585
1586
    return $recall;
1587
}
1588
1413
=head3 _type
1589
=head3 _type
1414
1590
1415
=cut
1591
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1910-1915 sub queue_notice { Link Here
1910
    return \%return;
1910
    return \%return;
1911
}
1911
}
1912
1912
1913
=head3 recalls
1914
1915
    my $recalls = $patron->recalls;
1916
1917
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1918
1919
Return the patron's active recalls - total, or on a specific biblio
1920
1921
=cut
1922
1923
sub recalls {
1924
    my ( $self, $params ) = @_;
1925
1926
    my $biblionumber = $params->{biblionumber};
1927
1928
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1929
1930
    if ( $biblionumber ) {
1931
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1932
    }
1933
1934
    return $recalls_rs;
1935
}
1936
1913
=head2 Internal methods
1937
=head2 Internal methods
1914
1938
1915
=head3 _type
1939
=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 1158-1162 subtest 'columns_to_str' => sub { Link Here
1158
    $cache->clear_from_cache("MarcSubfieldStructure-");
1158
    $cache->clear_from_cache("MarcSubfieldStructure-");
1159
1159
1160
    $schema->storage->txn_rollback;
1160
    $schema->storage->txn_rollback;
1161
};
1162
1163
subtest 'Recalls tests' => sub {
1164
1165
    plan tests => 20;
1166
1167
    $schema->storage->txn_begin;
1168
1169
    my $item1 = $builder->build_sample_item;
1170
    my $biblio = $item1->biblio;
1171
    my $branchcode = $item1->holdingbranch;
1172
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1173
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1174
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1175
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
1176
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
1177
1178
    my $recall1 = Koha::Recall->new({
1179
        borrowernumber => $patron1->borrowernumber,
1180
        recalldate => Koha::DateUtils::dt_from_string,
1181
        biblionumber => $biblio->biblionumber,
1182
        branchcode => $branchcode,
1183
        status => 'R',
1184
        itemnumber => $item1->itemnumber,
1185
        expirationdate => undef,
1186
        item_level_recall => 1
1187
    })->store;
1188
    my $recall2 = Koha::Recall->new({
1189
        borrowernumber => $patron2->borrowernumber,
1190
        recalldate => Koha::DateUtils::dt_from_string,
1191
        biblionumber => $biblio->biblionumber,
1192
        branchcode => $branchcode,
1193
        status => 'R',
1194
        itemnumber => $item1->itemnumber,
1195
        expirationdate => undef,
1196
        item_level_recall =>1
1197
    })->store;
1198
1199
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
1200
1201
    $recall2->set_cancelled;
1202
1203
    t::lib::Mocks::mock_preference('UseRecalls', 0);
1204
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
1205
1206
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1207
1208
    $item1->update({ notforloan => 1 });
1209
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
1210
    $item1->update({ notforloan => 0, itemlost => 1 });
1211
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
1212
    $item1->update({ itemlost => 0, withdrawn => 1 });
1213
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
1214
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
1215
1216
    $item1->update({ withdrawn => 0 });
1217
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1218
1219
    Koha::CirculationRules->set_rules({
1220
        branchcode => $branchcode,
1221
        categorycode => $patron1->categorycode,
1222
        itemtype => $item1->effective_itemtype,
1223
        rules => {
1224
            recalls_allowed => 0,
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 recalls_allowed = 0" );
1230
1231
    Koha::CirculationRules->set_rules({
1232
        branchcode => $branchcode,
1233
        categorycode => $patron1->categorycode,
1234
        itemtype => $item1->effective_itemtype,
1235
        rules => {
1236
            recalls_allowed => 1,
1237
            recalls_per_record => 1,
1238
            on_shelf_recalls => 'all',
1239
        },
1240
    });
1241
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
1242
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
1243
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
1244
1245
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
1246
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
1247
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
1248
1249
    $recall1->set_cancelled;
1250
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
1251
1252
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
1253
1254
    Koha::CirculationRules->set_rules({
1255
        branchcode => $branchcode,
1256
        categorycode => $patron1->categorycode,
1257
        itemtype => $item1->effective_itemtype,
1258
        rules => {
1259
            recalls_allowed => 1,
1260
            recalls_per_record => 1,
1261
            on_shelf_recalls => 'any',
1262
        },
1263
    });
1264
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
1265
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
1266
1267
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1268
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
1269
1270
    $recall1 = Koha::Recall->new({
1271
        borrowernumber => $patron1->borrowernumber,
1272
        recalldate => Koha::DateUtils::dt_from_string,
1273
        biblionumber => $biblio->biblionumber,
1274
        branchcode => $branchcode,
1275
        status => 'R',
1276
        itemnumber => undef,
1277
        expirationdate => undef,
1278
        item_level_recall => 0
1279
    })->store;
1280
1281
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
1282
1283
    Koha::CirculationRules->set_rules({
1284
        branchcode => undef,
1285
        categorycode => undef,
1286
        itemtype => $item1->effective_itemtype,
1287
        rules => {
1288
            recalls_allowed => 0,
1289
            recalls_per_record => 1,
1290
            on_shelf_recalls => 'any',
1291
        },
1292
    });
1293
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
1294
1295
    Koha::CirculationRules->set_rules({
1296
        branchcode => undef,
1297
        categorycode => undef,
1298
        itemtype => $item1->effective_itemtype,
1299
        rules => {
1300
            recalls_allowed => 1,
1301
            recalls_per_record => 1,
1302
            on_shelf_recalls => 'any',
1303
        },
1304
    });
1305
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
1306
1307
    # check_recalls tests
1308
1309
    $recall1 = Koha::Recall->new({
1310
        borrowernumber => $patron2->borrowernumber,
1311
        recalldate => Koha::DateUtils::dt_from_string,
1312
        biblionumber => $biblio->biblionumber,
1313
        branchcode => $branchcode,
1314
        status => 'R',
1315
        itemnumber => $item1->itemnumber,
1316
        expirationdate => undef,
1317
        item_level_recall => 1
1318
    })->store;
1319
    $recall2 = Koha::Recall->new({
1320
        borrowernumber => $patron1->borrowernumber,
1321
        recalldate => Koha::DateUtils::dt_from_string,
1322
        biblionumber => $biblio->biblionumber,
1323
        branchcode => $branchcode,
1324
        status => 'R',
1325
        itemnumber => undef,
1326
        expirationdate => undef,
1327
        item_level_recall => 0
1328
    })->store;
1329
    $recall2->set_waiting({ item => $item1 });
1330
1331
    # return a waiting recall
1332
    my $check_recall = $item1->check_recalls;
1333
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
1334
1335
    $recall2->revert_waiting;
1336
1337
    # return recall based on recalldate
1338
    $check_recall = $item1->check_recalls;
1339
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
1340
1341
    $recall1->set_cancelled;
1342
1343
    # return a biblio-level recall
1344
    $check_recall = $item1->check_recalls;
1345
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
1161
1346
1347
    $recall2->set_cancelled;
1162
};
1348
};
(-)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