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

(-)a/C4/Circulation.pm (-2 / +119 lines)
Lines 306-311 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
306
306
307
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
307
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
308
308
309
=item C<RecallPlacedAtHoldingBranch>
310
311
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.
312
313
=item C<RecallFound>
314
315
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
316
309
=back
317
=back
310
318
311
=back
319
=back
Lines 379-384 sub transferbook { Link Here
379
        $dotransfer = 1;
387
        $dotransfer = 1;
380
    }
388
    }
381
389
390
    # find recall
391
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
392
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
393
        # do a transfer if the recall branch is different to the item holding branch
394
        if ( $recall->branchcode eq $fbr ) {
395
            $dotransfer = 0;
396
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
397
        } else {
398
            $dotransfer = 1;
399
            $messages->{'RecallFound'} = $recall;
400
        }
401
    }
402
382
    #actually do the transfer....
403
    #actually do the transfer....
383
    if ($dotransfer) {
404
    if ($dotransfer) {
384
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
405
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 725-730 sticky due date is invalid or due date in the past Link Here
725
746
726
if the borrower borrows to much things
747
if the borrower borrows to much things
727
748
749
=head3 RECALLED
750
751
recalled by someone else
752
728
=cut
753
=cut
729
754
730
sub CanBookBeIssued {
755
sub CanBookBeIssued {
Lines 1098-1104 sub CanBookBeIssued { Link Here
1098
        }
1123
        }
1099
    }
1124
    }
1100
1125
1101
    unless ( $ignore_reserves ) {
1126
    my $recall;
1127
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1128
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1129
    # Don't look at recalls that are in transit
1130
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1131
        my @recalls = $biblio->recalls;
1132
1133
        foreach my $r ( @recalls ) {
1134
            if ( $r->itemnumber and
1135
                $r->itemnumber == $item_object->itemnumber and
1136
                $r->borrowernumber == $patron->borrowernumber and
1137
                $r->waiting ) {
1138
                $messages{RECALLED} = $r->recall_id;
1139
                $recall = $r;
1140
                # this item is already waiting for this borrower and the recall can be fulfilled
1141
                last;
1142
            }
1143
            elsif ( $r->itemnumber and
1144
                $r->itemnumber == $item_object->itemnumber and
1145
                $r->in_transit ) {
1146
                # recalled item is in transit
1147
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1148
            }
1149
            elsif ( $r->item_level_recall and
1150
                $r->itemnumber == $item_object->itemnumber and
1151
                $r->borrowernumber != $patron->borrowernumber and
1152
                !$r->in_transit ) {
1153
                # this specific item has been recalled by a different patron
1154
                $needsconfirmation{RECALLED} = $r;
1155
                $recall = $r;
1156
                last;
1157
            }
1158
            elsif ( !$r->item_level_recall and
1159
                $r->borrowernumber != $patron->borrowernumber and
1160
                !$r->in_transit ) {
1161
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1162
                $needsconfirmation{RECALLED} = $r;
1163
                $recall = $r;
1164
                last;
1165
            }
1166
        }
1167
    }
1168
1169
    unless ( $ignore_reserves and defined $recall ) {
1102
        # See if the item is on reserve.
1170
        # See if the item is on reserve.
1103
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1171
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1104
        if ($restype) {
1172
        if ($restype) {
Lines 1391-1396 AddIssue does the following things : Link Here
1391
          * RESERVE PLACED ?
1459
          * RESERVE PLACED ?
1392
              - fill reserve if reserve to this patron
1460
              - fill reserve if reserve to this patron
1393
              - cancel reserve or not, otherwise
1461
              - cancel reserve or not, otherwise
1462
          * RECALL PLACED ?
1463
              - fill recall if recall to this patron
1464
              - cancel recall or not
1465
              - revert recall's waiting status or not
1394
          * TRANSFERT PENDING ?
1466
          * TRANSFERT PENDING ?
1395
              - complete the transfert
1467
              - complete the transfert
1396
          * ISSUE THE BOOK
1468
          * ISSUE THE BOOK
Lines 1405-1410 sub AddIssue { Link Here
1405
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1477
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1406
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1478
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1407
    my $auto_renew = $params && $params->{auto_renew};
1479
    my $auto_renew = $params && $params->{auto_renew};
1480
    my $cancel_recall = $params && $params->{cancel_recall};
1481
    my $recall_id = $params && $params->{recall_id};
1408
    my $dbh          = C4::Context->dbh;
1482
    my $dbh          = C4::Context->dbh;
1409
    my $barcodecheck = CheckValidBarcode($barcode);
1483
    my $barcodecheck = CheckValidBarcode($barcode);
1410
1484
Lines 1476-1481 sub AddIssue { Link Here
1476
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1550
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1477
            }
1551
            }
1478
1552
1553
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1554
1479
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1555
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1480
1556
1481
            # Starting process for transfer job (checking transfert and validate it if we have one)
1557
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1923-1928 Value 1 if return is successful. Link Here
1923
1999
1924
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2000
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1925
2001
2002
=item C<RecallFound>
2003
2004
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2005
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2006
branchcode.
2007
2008
=item C<TransferredRecall>
2009
2010
This item has been transferred to this branch to fill a recall. The recall object is returned.
2011
1926
=back
2012
=back
1927
2013
1928
C<$iteminformation> is a reference-to-hash, giving information about the
2014
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2179-2184 sub AddReturn { Link Here
2179
        }
2265
        }
2180
    }
2266
    }
2181
2267
2268
    # find recalls...
2269
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2270
    my $recall = undef;
2271
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2272
    if ( defined $recall ) {
2273
        $messages->{RecallFound} = $recall;
2274
        if ( $recall->branchcode ne $branch ) {
2275
            $messages->{RecallNeedsTransfer} = $branch;
2276
        }
2277
    }
2278
2182
    # find reserves.....
2279
    # find reserves.....
2183
    # launch the Checkreserves routine to find any holds
2280
    # launch the Checkreserves routine to find any holds
2184
    my ($resfound, $resrec);
2281
    my ($resfound, $resrec);
Lines 2237-2244 sub AddReturn { Link Here
2237
        $request->status('RET') if $request;
2334
        $request->status('RET') if $request;
2238
    }
2335
    }
2239
2336
2337
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2338
    if ( $transfer_recall and
2339
         $transfer_recall->branchcode eq $branch and
2340
         C4::Context->preference('UseRecalls') ) {
2341
        $messages->{TransferredRecall} = $transfer_recall;
2342
    }
2343
2240
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2344
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2241
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
2345
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch)
2346
 and not $messages->{'WrongTransfer'} and not $messages->{TransferredRecall} and not $messages->{RecallNeedsTransfer} ){
2242
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2347
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2243
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2348
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2244
            (C4::Context->preference("UseBranchTransferLimits") and
2349
            (C4::Context->preference("UseBranchTransferLimits") and
Lines 2867-2872 sub CanBookBeRenewed { Link Here
2867
        }
2972
        }
2868
    }
2973
    }
2869
2974
2975
    my $recall = undef;
2976
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2977
    if ( defined $recall ) {
2978
        if ( $recall->item_level_recall ) {
2979
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2980
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2981
        } else {
2982
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2983
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2984
        }
2985
    }
2986
2870
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2987
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2871
2988
2872
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2989
    # 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 359-364 sub CanBookBeReserved{ Link Here
359
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
359
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
360
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
360
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
361
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
361
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
362
         { status => recall }, if the borrower has already placed a recall on this item
362
363
363
=cut
364
=cut
364
365
Lines 398-403 sub CanItemBeReserved { Link Here
398
        return { status =>'alreadypossession' };
399
        return { status =>'alreadypossession' };
399
    }
400
    }
400
401
402
    # check if a recall exists on this item from this borrower
403
    return { status => 'recall' }
404
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
405
401
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
406
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
402
407
403
    my $querycount = q{
408
    my $querycount = q{
(-)a/C4/Search.pm (+9 lines)
Lines 1892-1897 sub searchResults { Link Here
1892
        my $can_place_holds       = 0;
1892
        my $can_place_holds       = 0;
1893
        my $item_onhold_count     = 0;
1893
        my $item_onhold_count     = 0;
1894
        my $notforloan_count      = 0;
1894
        my $notforloan_count      = 0;
1895
        my $item_recalled_count   = 0;
1895
        my $items_count           = scalar(@fields);
1896
        my $items_count           = scalar(@fields);
1896
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1897
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1897
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1898
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1986-1991 sub searchResults { Link Here
1986
                # is item on the reserve shelf?
1987
                # is item on the reserve shelf?
1987
                my $reservestatus = '';
1988
                my $reservestatus = '';
1988
1989
1990
                # is item a waiting recall?
1991
                my $recallstatus = '';
1992
1989
                unless ($item->{withdrawn}
1993
                unless ($item->{withdrawn}
1990
                        || $item->{itemlost}
1994
                        || $item->{itemlost}
1991
                        || $item->{damaged}
1995
                        || $item->{damaged}
Lines 2007-2012 sub searchResults { Link Here
2007
                    #
2011
                    #
2008
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2012
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
2009
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2013
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
2014
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
2010
                }
2015
                }
2011
2016
2012
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
2017
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 2015-2020 sub searchResults { Link Here
2015
                    || $item->{damaged}
2020
                    || $item->{damaged}
2016
                    || $item->{notforloan}
2021
                    || $item->{notforloan}
2017
                    || $reservestatus eq 'Waiting'
2022
                    || $reservestatus eq 'Waiting'
2023
                    || $recallstatus eq 'Waiting'
2018
                    || ($transfertwhen && $transfertwhen ne ''))
2024
                    || ($transfertwhen && $transfertwhen ne ''))
2019
                {
2025
                {
2020
                    $withdrawn_count++        if $item->{withdrawn};
2026
                    $withdrawn_count++        if $item->{withdrawn};
Lines 2022-2027 sub searchResults { Link Here
2022
                    $itemdamaged_count++     if $item->{damaged};
2028
                    $itemdamaged_count++     if $item->{damaged};
2023
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2029
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
2024
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2030
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
2031
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
2025
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2032
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
2026
2033
2027
                    # can place a hold on a item if
2034
                    # can place a hold on a item if
Lines 2043-2048 sub searchResults { Link Here
2043
                        $other_items->{$key}->{$_} = $item->{$_};
2050
                        $other_items->{$key}->{$_} = $item->{$_};
2044
                    }
2051
                    }
2045
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2052
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
2053
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
2046
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2054
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
2047
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2055
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
2048
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
2056
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2117-2122 sub searchResults { Link Here
2117
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2125
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2118
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2126
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2119
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2127
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2128
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2120
        $oldbiblio->{orderedcount}         = $ordered_count;
2129
        $oldbiblio->{orderedcount}         = $ordered_count;
2121
        $oldbiblio->{notforloancount}      = $notforloan_count;
2130
        $oldbiblio->{notforloancount}      = $notforloan_count;
2122
2131
(-)a/C4/XSLT.pm (-1 / +7 lines)
Lines 321-327 sub buildKohaItemsNamespace { Link Here
321
        my $status;
321
        my $status;
322
        my $substatus = '';
322
        my $substatus = '';
323
323
324
        if ($item->has_pending_hold) {
324
        my $recalls = Koha::Recalls->search({ itemnumber => $item->itemnumber, status => 'W' });
325
326
        if ( $recalls->count ) {
327
            # recalls take priority over holds
328
            $status = 'Waiting';
329
        }
330
        elsif ( $item->has_pending_hold ) {
325
            $status = 'Pending hold';
331
            $status = 'Pending hold';
326
        }
332
        }
327
        elsif ( $item->holds->waiting->count ) {
333
        elsif ( $item->holds->waiting->count ) {
(-)a/Koha/Biblio.pm (+108 lines)
Lines 832-837 sub to_api_mapping { Link Here
832
    };
832
    };
833
}
833
}
834
834
835
=head3 recalls
836
837
    my @recalls = $biblio->recalls;
838
839
Return all active recalls attached to this biblio, sorted by oldest first
840
841
=cut
842
843
sub recalls {
844
    my ( $self ) = @_;
845
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
846
    return @recalls_rs;
847
}
848
849
=head3 can_be_recalled
850
851
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
852
853
Does biblio-level checks and returns the items attached to this biblio that are available for recall
854
855
=cut
856
857
sub can_be_recalled {
858
    my ( $self, $params ) = @_;
859
860
    return 0 if !( C4::Context->preference('UseRecalls') );
861
862
    my $patron = $params->{patron};
863
864
    my $branchcode = C4::Context->userenv->{'branch'};
865
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
866
        $branchcode = $patron->branchcode;
867
    }
868
869
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
870
871
    # if there are no available items at all, no recall can be placed
872
    return 0 if ( scalar @all_items == 0 );
873
874
    my @itemtypes;
875
    my @itemnumbers;
876
    my @items;
877
    foreach my $item ( @all_items ) {
878
        if ( $item->can_be_recalled({ patron => $patron }) ) {
879
            push( @itemtypes, $item->effective_itemtype );
880
            push( @itemnumbers, $item->itemnumber );
881
            push( @items, $item );
882
        }
883
    }
884
885
    # if there are no recallable items, no recall can be placed
886
    return 0 if ( scalar @items == 0 );
887
888
    # Check the circulation rule for each relevant itemtype for this biblio
889
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
890
    foreach my $itemtype ( @itemtypes ) {
891
        my $rule = Koha::CirculationRules->get_effective_rules({
892
            branchcode => $branchcode,
893
            categorycode => $patron ? $patron->categorycode : undef,
894
            itemtype => $itemtype,
895
            rules => [
896
                'recalls_allowed',
897
                'recalls_per_record',
898
                'on_shelf_recalls',
899
            ],
900
        });
901
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
902
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
903
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
904
    }
905
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
906
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
907
    my %on_shelf_recalls_count = ();
908
    foreach my $count ( @on_shelf_recalls ) {
909
        $on_shelf_recalls_count{$count}++;
910
    }
911
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
912
913
    # check recalls allowed has been set and is not zero
914
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
915
916
    if ( $patron ) {
917
        # check borrower has not reached open recalls allowed limit
918
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
919
920
        # check borrower has not reached open recalls allowed per record limit
921
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
922
923
        # check if any of the items under this biblio are already checked out by this borrower
924
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
925
    }
926
927
    # check item availability
928
    my $checked_out_count = 0;
929
    foreach (@items) {
930
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
931
    }
932
933
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
934
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
935
936
    # can't recall if no items have been checked out
937
    return 0 if ( $checked_out_count == 0 );
938
939
    # can recall
940
    return @items;
941
}
942
835
=head2 Internal methods
943
=head2 Internal methods
836
944
837
=head3 type
945
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1055-1060 sub _after_item_action_hooks { Link Here
1055
    );
1055
    );
1056
}
1056
}
1057
1057
1058
=head3 recall
1059
1060
    my $recall = $item->recall;
1061
1062
Return the relevant recall for this item
1063
1064
=cut
1065
1066
sub recall {
1067
    my ( $self ) = @_;
1068
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1069
    foreach my $recall (@recalls) {
1070
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1071
            return $recall;
1072
        }
1073
    }
1074
    # no item-level recall to return, so return earliest biblio-level
1075
    # FIXME: eventually this will be based on priority
1076
    return $recalls[0];
1077
}
1078
1079
=head3 can_be_recalled
1080
1081
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1082
1083
Does item-level checks and returns if items can be recalled by this borrower
1084
1085
=cut
1086
1087
sub can_be_recalled {
1088
    my ( $self, $params ) = @_;
1089
1090
    return 0 if !( C4::Context->preference('UseRecalls') );
1091
1092
    # check if this item is not for loan, withdrawn or lost
1093
    return 0 if ( $self->notforloan != 0 );
1094
    return 0 if ( $self->itemlost != 0 );
1095
    return 0 if ( $self->withdrawn != 0 );
1096
1097
    # check if this item is not checked out - if not checked out, can't be recalled
1098
    return 0 if ( !defined( $self->checkout ) );
1099
1100
    my $patron = $params->{patron};
1101
1102
    my $branchcode = C4::Context->userenv->{'branch'};
1103
    if ( $patron ) {
1104
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1105
    }
1106
1107
    # Check the circulation rule for each relevant itemtype for this item
1108
    my $rule = Koha::CirculationRules->get_effective_rules({
1109
        branchcode => $branchcode,
1110
        categorycode => $patron ? $patron->categorycode : undef,
1111
        itemtype => $self->effective_itemtype,
1112
        rules => [
1113
            'recalls_allowed',
1114
            'recalls_per_record',
1115
            'on_shelf_recalls',
1116
        ],
1117
    });
1118
1119
    # check recalls allowed has been set and is not zero
1120
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1121
1122
    if ( $patron ) {
1123
        # check borrower has not reached open recalls allowed limit
1124
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1125
1126
        # check borrower has not reach open recalls allowed per record limit
1127
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1128
1129
        # check if this patron has already recalled this item
1130
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1131
1132
        # check if this patron has already checked out this item
1133
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1134
1135
        # check if this patron has already reserved this item
1136
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1137
    }
1138
1139
    # check item availability
1140
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1141
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1142
1143
    # if there are no available items at all, no recall can be placed
1144
    return 0 if ( scalar @items == 0 );
1145
1146
    my $checked_out_count = 0;
1147
    foreach (@items) {
1148
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1149
    }
1150
1151
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1152
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1153
1154
    # can't recall if no items have been checked out
1155
    return 0 if ( $checked_out_count == 0 );
1156
1157
    # can recall
1158
    return 1;
1159
}
1160
1161
=head3 can_be_waiting_recall
1162
1163
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1164
1165
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1166
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1167
1168
=cut
1169
1170
sub can_be_waiting_recall {
1171
    my ( $self ) = @_;
1172
1173
    return 0 if !( C4::Context->preference('UseRecalls') );
1174
1175
    # check if this item is not for loan, withdrawn or lost
1176
    return 0 if ( $self->notforloan != 0 );
1177
    return 0 if ( $self->itemlost != 0 );
1178
    return 0 if ( $self->withdrawn != 0 );
1179
1180
    my $branchcode = $self->holdingbranch;
1181
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1182
        $branchcode = C4::Context->userenv->{'branch'};
1183
    } else {
1184
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1185
    }
1186
1187
    # Check the circulation rule for each relevant itemtype for this item
1188
    my $rule = Koha::CirculationRules->get_effective_rules({
1189
        branchcode => $branchcode,
1190
        categorycode => undef,
1191
        itemtype => $self->effective_itemtype,
1192
        rules => [
1193
            'recalls_allowed',
1194
        ],
1195
    });
1196
1197
    # check recalls allowed has been set and is not zero
1198
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1199
1200
    # can recall
1201
    return 1;
1202
}
1203
1204
=head3 check_recalls
1205
1206
    my $recall = $item->check_recalls;
1207
1208
Get the most relevant recall for this item.
1209
1210
=cut
1211
1212
sub check_recalls {
1213
    my ( $self ) = @_;
1214
1215
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1216
1217
    my $recall;
1218
    # iterate through relevant recalls to find the best one.
1219
    # if we come across a waiting recall, use this one.
1220
    # 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.
1221
    foreach my $r ( @recalls ) {
1222
        if ( $r->waiting ) {
1223
            $recall = $r;
1224
            last;
1225
        }
1226
    }
1227
    unless ( defined $recall ) {
1228
        $recall = $recalls[0];
1229
    }
1230
1231
    return $recall;
1232
}
1233
1058
=head3 _type
1234
=head3 _type
1059
1235
1060
=cut
1236
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1785-1790 sub to_api_mapping { Link Here
1785
    };
1785
    };
1786
}
1786
}
1787
1787
1788
=head3 recalls
1789
1790
    my $recalls = $patron->recalls;
1791
1792
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1793
1794
Return the patron's active recalls - total, or on a specific biblio
1795
1796
=cut
1797
1798
sub recalls {
1799
    my ( $self, $params ) = @_;
1800
1801
    my $biblionumber = $params->{biblionumber};
1802
1803
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1804
1805
    if ( $biblionumber ) {
1806
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1807
    }
1808
1809
    return $recalls_rs;
1810
}
1811
1788
=head2 Internal methods
1812
=head2 Internal methods
1789
1813
1790
=head3 _type
1814
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 27-32 use Koha::Biblios; Link Here
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::ArticleRequest::Status;
29
use Koha::ArticleRequest::Status;
30
use Koha::Recalls;
30
31
31
sub HoldsCount {
32
sub HoldsCount {
32
    my ( $self, $biblionumber ) = @_;
33
    my ( $self, $biblionumber ) = @_;
Lines 63-66 sub CanArticleRequest { Link Here
63
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
64
}
65
}
65
66
67
sub RecallsCount {
68
    my ( $self, $biblionumber ) = @_;
69
70
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
71
72
    return $recalls->count;
73
}
74
66
1;
75
1;
(-)a/t/db_dependent/Circulation.t (-1 / +258 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 => 51;
21
use Test::More tests => 54;
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 1263-1268 subtest "CanBookBeRenewed tests" => sub { Link Here
1263
            $item_3->itemcallnumber || '' ),
1263
            $item_3->itemcallnumber || '' ),
1264
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1264
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1265
    );
1265
    );
1266
1267
    # Recalls
1268
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1269
    Koha::CirculationRules->set_rules({
1270
        categorycode => undef,
1271
        branchcode => undef,
1272
        itemtype => undef,
1273
        rules => {
1274
            recalls_allowed => 10,
1275
            renewalsallowed => 5,
1276
        },
1277
    });
1278
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1279
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1280
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1281
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1282
1283
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1284
1285
    # item-level and this item: renewal not allowed
1286
    my $recall = Koha::Recall->new({
1287
        biblionumber => $recall_item1->biblionumber,
1288
        itemnumber => $recall_item1->itemnumber,
1289
        borrowernumber => $recall_borrower->borrowernumber,
1290
        branchcode => $recall_borrower->branchcode,
1291
        item_level_recall => 1,
1292
        status => 'R',
1293
    })->store;
1294
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1295
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1296
    $recall->set_cancelled;
1297
1298
    # biblio-level requested recall: renewal not allowed
1299
    $recall = Koha::Recall->new({
1300
        biblionumber => $recall_item1->biblionumber,
1301
        itemnumber => undef,
1302
        borrowernumber => $recall_borrower->borrowernumber,
1303
        branchcode => $recall_borrower->branchcode,
1304
        item_level_recall => 0,
1305
        status => 'R',
1306
    })->store;
1307
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1308
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1309
    $recall->set_cancelled;
1310
1311
    # item-level and not this item: renewal allowed
1312
    $recall = Koha::Recall->new({
1313
        biblionumber => $recall_item2->biblionumber,
1314
        itemnumber => $recall_item2->itemnumber,
1315
        borrowernumber => $recall_borrower->borrowernumber,
1316
        branchcode => $recall_borrower->branchcode,
1317
        item_level_recall => 1,
1318
        status => 'R',
1319
    })->store;
1320
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1321
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1322
    $recall->set_cancelled;
1323
1324
    # biblio-level waiting recall: renewal allowed
1325
    $recall = Koha::Recall->new({
1326
        biblionumber => $recall_item1->biblionumber,
1327
        itemnumber => undef,
1328
        borrowernumber => $recall_borrower->borrowernumber,
1329
        branchcode => $recall_borrower->branchcode,
1330
        item_level_recall => 0,
1331
        status => 'R',
1332
    })->store;
1333
    $recall->set_waiting({ item => $recall_item1 });
1334
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1335
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1336
    $recall->set_cancelled;
1266
};
1337
};
1267
1338
1268
subtest "GetUpcomingDueIssues" => sub {
1339
subtest "GetUpcomingDueIssues" => sub {
Lines 1743-1748 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1743
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1814
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1744
};
1815
};
1745
1816
1817
subtest 'AddIssue | recalls' => sub {
1818
    plan tests => 3;
1819
1820
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1821
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1822
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1823
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1824
    my $item = $builder->build_sample_item;
1825
    Koha::CirculationRules->set_rules({
1826
        branchcode => undef,
1827
        itemtype => undef,
1828
        categorycode => undef,
1829
        rules => {
1830
            recalls_allowed => 10,
1831
        },
1832
    });
1833
1834
    # checking out item that they have recalled
1835
    my $recall1 = Koha::Recall->new({
1836
        borrowernumber => $patron1->borrowernumber,
1837
        biblionumber => $item->biblionumber,
1838
        itemnumber => $item->itemnumber,
1839
        item_level_recall => 1,
1840
        branchcode => $patron1->branchcode,
1841
        status => 'R',
1842
    })->store;
1843
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1844
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1845
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1846
    AddReturn( $item->barcode, $item->homebranch );
1847
1848
    # this item is has a recall request. cancel recall
1849
    my $recall2 = Koha::Recall->new({
1850
        borrowernumber => $patron2->borrowernumber,
1851
        biblionumber => $item->biblionumber,
1852
        itemnumber => $item->itemnumber,
1853
        item_level_recall => 1,
1854
        branchcode => $patron2->branchcode,
1855
        status => 'R',
1856
    })->store;
1857
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1858
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1859
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1860
    AddReturn( $item->barcode, $item->homebranch );
1861
1862
    # this item is waiting to fulfill a recall. revert recall
1863
    my $recall3 = Koha::Recall->new({
1864
        borrowernumber => $patron2->borrowernumber,
1865
        biblionumber => $item->biblionumber,
1866
        itemnumber => $item->itemnumber,
1867
        item_level_recall => 1,
1868
        branchcode => $patron2->branchcode,
1869
        status => 'R',
1870
    })->store;
1871
    $recall3->set_waiting;
1872
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1873
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1874
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1875
    AddReturn( $item->barcode, $item->homebranch );
1876
};
1877
1878
1746
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1879
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1747
    plan tests => 8;
1880
    plan tests => 8;
1748
1881
Lines 3646-3651 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3646
    # TODO test with AllowNotForLoanOverride = 1
3779
    # TODO test with AllowNotForLoanOverride = 1
3647
};
3780
};
3648
3781
3782
subtest 'CanBookBeIssued | recalls' => sub {
3783
    plan tests => 3;
3784
3785
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3786
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3787
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3788
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3789
    my $item = $builder->build_sample_item;
3790
    Koha::CirculationRules->set_rules({
3791
        branchcode => undef,
3792
        itemtype => undef,
3793
        categorycode => undef,
3794
        rules => {
3795
            recalls_allowed => 10,
3796
        },
3797
    });
3798
3799
    # item-level recall
3800
    my $recall = Koha::Recall->new({
3801
        borrowernumber => $patron1->borrowernumber,
3802
        biblionumber => $item->biblionumber,
3803
        itemnumber => $item->itemnumber,
3804
        item_level_recall => 1,
3805
        branchcode => $patron1->branchcode,
3806
        status => 'R',
3807
    })->store;
3808
3809
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3810
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3811
3812
    $recall->set_cancelled;
3813
3814
    # biblio-level recall
3815
    $recall = Koha::Recall->new({
3816
        borrowernumber => $patron1->borrowernumber,
3817
        biblionumber => $item->biblionumber,
3818
        itemnumber => undef,
3819
        item_level_recall => 0,
3820
        branchcode => $patron1->branchcode,
3821
        status => 'R',
3822
    })->store;
3823
3824
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3825
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3826
3827
    $recall->set_cancelled;
3828
3829
    # biblio-level recall
3830
    $recall = Koha::Recall->new({
3831
        borrowernumber => $patron1->borrowernumber,
3832
        biblionumber => $item->biblionumber,
3833
        itemnumber => undef,
3834
        item_level_recall => 0,
3835
        branchcode => $patron1->branchcode,
3836
        status => 'R',
3837
    })->store;
3838
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
3839
3840
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
3841
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
3842
3843
    $recall->set_cancelled;
3844
};
3845
3649
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3846
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3650
    plan tests => 1;
3847
    plan tests => 1;
3651
3848
Lines 3661-3666 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3661
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3858
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3662
};
3859
};
3663
3860
3861
subtest 'AddReturn | recalls' => sub {
3862
    plan tests => 3;
3863
3864
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3865
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3866
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3867
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3868
    my $item1 = $builder->build_sample_item;
3869
    Koha::CirculationRules->set_rules({
3870
        branchcode => undef,
3871
        itemtype => undef,
3872
        categorycode => undef,
3873
        rules => {
3874
            recalls_allowed => 10,
3875
        },
3876
    });
3877
3878
    # this item can fill a recall with pickup at this branch
3879
    AddIssue( $patron1->unblessed, $item1->barcode );
3880
    my $recall1 = Koha::Recall->new({
3881
        borrowernumber => $patron2->borrowernumber,
3882
        biblionumber => $item1->biblionumber,
3883
        itemnumber => $item1->itemnumber,
3884
        item_level_recall => 1,
3885
        branchcode => $item1->homebranch,
3886
        status => 'R',
3887
    })->store;
3888
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3889
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3890
    $recall1->set_cancelled;
3891
3892
    # this item can fill a recall but needs transfer
3893
    AddIssue( $patron1->unblessed, $item1->barcode );
3894
    $recall1 = Koha::Recall->new({
3895
        borrowernumber => $patron2->borrowernumber,
3896
        biblionumber => $item1->biblionumber,
3897
        itemnumber => $item1->itemnumber,
3898
        item_level_recall => 1,
3899
        branchcode => $patron2->branchcode,
3900
        status => 'R',
3901
    })->store;
3902
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3903
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3904
    $recall1->set_cancelled;
3905
3906
    # this item is already in transit, do not ask to transfer
3907
    AddIssue( $patron1->unblessed, $item1->barcode );
3908
    $recall1 = Koha::Recall->new({
3909
        borrowernumber => $patron2->borrowernumber,
3910
        biblionumber => $item1->biblionumber,
3911
        itemnumber => $item1->itemnumber,
3912
        item_level_recall => 1,
3913
        branchcode => $patron2->branchcode,
3914
        status => 'R',
3915
    })->store;
3916
    $recall1->start_transfer;
3917
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3918
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3919
    $recall1->set_cancelled;
3920
};
3664
3921
3665
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3922
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3666
3923
(-)a/t/db_dependent/Circulation/transferbook.t (-1 / +32 lines)
Lines 27-32 use Koha::DateUtils qw( dt_from_string ); Link Here
27
use Koha::Item::Transfers;
27
use Koha::Item::Transfers;
28
28
29
my $builder = t::lib::TestBuilder->new;
29
my $builder = t::lib::TestBuilder->new;
30
my $schema = Koha::Database->new->schema;
31
32
$schema->storage->txn_begin;
30
33
31
subtest 'transfer a non-existant item' => sub {
34
subtest 'transfer a non-existant item' => sub {
32
    plan tests => 2;
35
    plan tests => 2;
Lines 99-105 subtest 'field population tests' => sub { Link Here
99
#FIXME:'UseBranchTransferLimits tests missing
102
#FIXME:'UseBranchTransferLimits tests missing
100
103
101
subtest 'transfer already at destination' => sub {
104
subtest 'transfer already at destination' => sub {
102
    plan tests => 5;
105
    plan tests => 9;
103
106
104
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
107
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
105
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
108
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
Lines 147-152 subtest 'transfer already at destination' => sub { Link Here
147
    is( $dotransfer, 1, 'Transfer of reserved item succeeded without ignore reserves' );
150
    is( $dotransfer, 1, 'Transfer of reserved item succeeded without ignore reserves' );
148
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
151
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
149
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
152
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
153
154
    # recalls
155
    t::lib::Mocks::mock_preference('UseRecalls', 1);
156
    my $recall = Koha::Recall->new({
157
        biblionumber => $item->biblionumber,
158
        itemnumber => $item->itemnumber,
159
        item_level_recall => 1,
160
        borrowernumber => $patron->borrowernumber,
161
        branchcode => $library->branchcode,
162
        status => 'R',
163
    })->store;
164
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
165
    is( $dotransfer, 0, 'Do not transfer recalled item, it has already arrived' );
166
    is( $messages->{RecallPlacedAtHoldingBranch}, 1, "We found the recall");
167
168
    my $item2 = $builder->build_object({ class => 'Koha::Items' }); # this item will have a different holding branch to the pickup branch
169
    $recall = Koha::Recall->new({
170
        biblionumber => $item2->biblionumber,
171
        itemnumber => $item2->itemnumber,
172
        item_level_recall => 1,
173
        borrowernumber => $patron->borrowernumber,
174
        branchcode => $library->branchcode,
175
        status => 'R',
176
    })->store;
177
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
178
    is( $dotransfer, 1, 'Transfer of recalled item succeeded' );
179
    is( $messages->{RecallFound}->recall_id, $recall->recall_id, "We found the recall");
150
};
180
};
151
181
152
subtest 'transfer an issued item' => sub {
182
subtest 'transfer an issued item' => sub {
Lines 294-296 subtest 'transferbook test from branch' => sub { Link Here
294
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
324
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
295
325
296
};
326
};
327
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (-1 / +28 lines)
Lines 7-13 use t::lib::TestBuilder; Link Here
7
7
8
use C4::Context;
8
use C4::Context;
9
9
10
use Test::More tests => 67;
10
use Test::More tests => 68;
11
use MARC::Record;
11
use MARC::Record;
12
12
13
use C4::Biblio;
13
use C4::Biblio;
Lines 1326-1330 subtest 'non priority holds' => sub { Link Here
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1326
    is( $err, 'on_reserve', 'Item is on hold' );
1327
1327
1328
    $schema->storage->txn_rollback;
1328
    $schema->storage->txn_rollback;
1329
};
1330
1331
subtest 'CanItemBeReserved / recall' => sub {
1332
    plan tests => 1;
1333
1334
    $schema->storage->txn_begin;
1335
1336
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1337
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1338
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1339
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1340
    my $item1   = $builder->build_sample_item(
1341
        {
1342
            biblionumber => $biblio1->biblionumber,
1343
            library      => $library1->branchcode
1344
        }
1345
    );
1346
    Koha::Recall->new({
1347
        borrowernumber => $patron1->borrowernumber,
1348
        biblionumber => $biblio1->biblionumber,
1349
        branchcode => $library1->branchcode,
1350
        itemnumber => $item1->itemnumber,
1351
        recalldate => '2020-05-04 10:10:10',
1352
        item_level_recall => 1,
1353
    })->store;
1354
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1329
1355
1356
    $schema->storage->txn_rollback;
1330
};
1357
};
(-)a/t/db_dependent/Koha/Biblio.t (-2 / +119 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 12;
20
use Test::More tests => 13;
21
21
22
use C4::Biblio;
22
use C4::Biblio;
23
use Koha::Database;
23
use Koha::Database;
Lines 193-203 subtest 'pickup_locations' => sub { Link Here
193
193
194
    # Cleanup database
194
    # Cleanup database
195
    Koha::Holds->search->delete;
195
    Koha::Holds->search->delete;
196
    $dbh->do('DELETE FROM issues');
196
    Koha::Patrons->search->delete;
197
    Koha::Patrons->search->delete;
197
    Koha::Items->search->delete;
198
    Koha::Items->search->delete;
198
    Koha::Libraries->search->delete;
199
    Koha::Libraries->search->delete;
199
    Koha::CirculationRules->search->delete;
200
    Koha::CirculationRules->search->delete;
200
    $dbh->do('DELETE FROM issues');
201
    Koha::CirculationRules->set_rules(
201
    Koha::CirculationRules->set_rules(
202
        {
202
        {
203
            categorycode => undef,
203
            categorycode => undef,
Lines 558-560 subtest 'subscriptions() tests' => sub { Link Here
558
558
559
    $schema->storage->txn_rollback;
559
    $schema->storage->txn_rollback;
560
};
560
};
561
562
subtest 'Recalls tests' => sub {
563
564
    plan tests => 12;
565
566
    $schema->storage->txn_begin;
567
    my $item1 = $builder->build_sample_item;
568
    my $biblio = $item1->biblio;
569
    my $branchcode = $item1->holdingbranch;
570
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
571
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
572
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
573
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
574
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
575
576
    my $recall1 = Koha::Recall->new({
577
        borrowernumber => $patron1->borrowernumber,
578
        recalldate => Koha::DateUtils::dt_from_string,
579
        biblionumber => $biblio->biblionumber,
580
        branchcode => $branchcode,
581
        status => 'R',
582
        itemnumber => $item1->itemnumber,
583
        expirationdate => undef,
584
        item_level_recall => 1
585
    })->store;
586
    my $recall2 = Koha::Recall->new({
587
        borrowernumber => $patron2->borrowernumber,
588
        recalldate => Koha::DateUtils::dt_from_string,
589
        biblionumber => $biblio->biblionumber,
590
        branchcode => $branchcode,
591
        status => 'R',
592
        itemnumber => undef,
593
        expirationdate => undef,
594
        item_level_recall => 0
595
    })->store;
596
    my $recall3 = Koha::Recall->new({
597
        borrowernumber => $patron3->borrowernumber,
598
        recalldate => Koha::DateUtils::dt_from_string,
599
        biblionumber => $biblio->biblionumber,
600
        branchcode => $branchcode,
601
        status => 'R',
602
        itemnumber => $item1->itemnumber,
603
        expirationdate => undef,
604
        item_level_recall => 1
605
    })->store;
606
607
    my $recalls_count = scalar $biblio->recalls;
608
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
609
610
    $recall1->set_cancelled;
611
    $recall2->set_expired({ interface => 'COMMANDLINE' });
612
613
    $recalls_count = scalar $biblio->recalls;
614
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
615
616
    t::lib::Mocks::mock_preference('UseRecalls', 0);
617
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
618
619
    t::lib::Mocks::mock_preference("UseRecalls", 1);
620
    $item1->update({ notforloan => 1 });
621
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
622
623
    $item1->update({ notforloan => 0 });
624
    Koha::CirculationRules->set_rules({
625
        branchcode => $branchcode,
626
        categorycode => $patron1->categorycode,
627
        itemtype => $item1->effective_itemtype,
628
        rules => {
629
            recalls_allowed => 0,
630
            recalls_per_record => 1,
631
            on_shelf_recalls => 'all',
632
        },
633
    });
634
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
635
636
    Koha::CirculationRules->set_rules({
637
        branchcode => $branchcode,
638
        categorycode => $patron1->categorycode,
639
        itemtype => $item1->effective_itemtype,
640
        rules => {
641
            recalls_allowed => 1,
642
            recalls_per_record => 1,
643
            on_shelf_recalls => 'all',
644
        },
645
    });
646
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
647
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
648
649
    $recall1->set_cancelled;
650
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
651
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
652
653
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
654
655
    Koha::CirculationRules->set_rules({
656
        branchcode => $branchcode,
657
        categorycode => $patron1->categorycode,
658
        itemtype => $item1->effective_itemtype,
659
        rules => {
660
            recalls_allowed => 1,
661
            recalls_per_record => 1,
662
            on_shelf_recalls => 'any',
663
        },
664
    });
665
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
666
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
667
668
    $recall2->set_cancelled;
669
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
670
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
671
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
672
673
    $item1->update({ withdrawn => 1 });
674
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
675
676
    $schema->storage->txn_rollback;
677
};
(-)a/t/db_dependent/Koha/Item.t (-1 / +188 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 7;
22
use Test::More tests => 8;
23
23
24
use C4::Biblio;
24
use C4::Biblio;
25
use C4::Circulation;
25
use C4::Circulation;
Lines 516-518 subtest 'Tests for itemtype' => sub { Link Here
516
516
517
    $schema->storage->txn_rollback;
517
    $schema->storage->txn_rollback;
518
};
518
};
519
520
subtest 'Recalls tests' => sub {
521
522
    plan tests => 20;
523
524
    $schema->storage->txn_begin;
525
526
    my $item1 = $builder->build_sample_item;
527
    my $biblio = $item1->biblio;
528
    my $branchcode = $item1->holdingbranch;
529
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
530
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
531
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
532
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
533
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
534
535
    my $recall1 = Koha::Recall->new({
536
        borrowernumber => $patron1->borrowernumber,
537
        recalldate => Koha::DateUtils::dt_from_string,
538
        biblionumber => $biblio->biblionumber,
539
        branchcode => $branchcode,
540
        status => 'R',
541
        itemnumber => $item1->itemnumber,
542
        expirationdate => undef,
543
        item_level_recall => 1
544
    })->store;
545
    my $recall2 = Koha::Recall->new({
546
        borrowernumber => $patron2->borrowernumber,
547
        recalldate => Koha::DateUtils::dt_from_string,
548
        biblionumber => $biblio->biblionumber,
549
        branchcode => $branchcode,
550
        status => 'R',
551
        itemnumber => $item1->itemnumber,
552
        expirationdate => undef,
553
        item_level_recall =>1
554
    })->store;
555
556
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
557
558
    $recall2->set_cancelled;
559
560
    t::lib::Mocks::mock_preference('UseRecalls', 0);
561
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
562
563
    t::lib::Mocks::mock_preference("UseRecalls", 1);
564
565
    $item1->update({ notforloan => 1 });
566
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
567
    $item1->update({ notforloan => 0, itemlost => 1 });
568
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
569
    $item1->update({ itemlost => 0, withdrawn => 1 });
570
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
571
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
572
573
    $item1->update({ withdrawn => 0 });
574
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
575
576
    Koha::CirculationRules->set_rules({
577
        branchcode => $branchcode,
578
        categorycode => $patron1->categorycode,
579
        itemtype => $item1->effective_itemtype,
580
        rules => {
581
            recalls_allowed => 0,
582
            recalls_per_record => 1,
583
            on_shelf_recalls => 'all',
584
        },
585
    });
586
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
587
588
    Koha::CirculationRules->set_rules({
589
        branchcode => $branchcode,
590
        categorycode => $patron1->categorycode,
591
        itemtype => $item1->effective_itemtype,
592
        rules => {
593
            recalls_allowed => 1,
594
            recalls_per_record => 1,
595
            on_shelf_recalls => 'all',
596
        },
597
    });
598
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
599
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
600
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
601
602
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
603
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
604
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
605
606
    $recall1->set_cancelled;
607
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
608
609
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
610
611
    Koha::CirculationRules->set_rules({
612
        branchcode => $branchcode,
613
        categorycode => $patron1->categorycode,
614
        itemtype => $item1->effective_itemtype,
615
        rules => {
616
            recalls_allowed => 1,
617
            recalls_per_record => 1,
618
            on_shelf_recalls => 'any',
619
        },
620
    });
621
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
622
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
623
624
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
625
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
626
627
    $recall1 = Koha::Recall->new({
628
        borrowernumber => $patron1->borrowernumber,
629
        recalldate => Koha::DateUtils::dt_from_string,
630
        biblionumber => $biblio->biblionumber,
631
        branchcode => $branchcode,
632
        status => 'R',
633
        itemnumber => undef,
634
        expirationdate => undef,
635
        item_level_recall => 0
636
    })->store;
637
638
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
639
640
    Koha::CirculationRules->set_rules({
641
        branchcode => undef,
642
        categorycode => undef,
643
        itemtype => $item1->effective_itemtype,
644
        rules => {
645
            recalls_allowed => 0,
646
            recalls_per_record => 1,
647
            on_shelf_recalls => 'any',
648
        },
649
    });
650
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
651
652
    Koha::CirculationRules->set_rules({
653
        branchcode => undef,
654
        categorycode => undef,
655
        itemtype => $item1->effective_itemtype,
656
        rules => {
657
            recalls_allowed => 1,
658
            recalls_per_record => 1,
659
            on_shelf_recalls => 'any',
660
        },
661
    });
662
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
663
664
    # check_recalls tests
665
666
    $recall1 = Koha::Recall->new({
667
        borrowernumber => $patron2->borrowernumber,
668
        recalldate => Koha::DateUtils::dt_from_string,
669
        biblionumber => $biblio->biblionumber,
670
        branchcode => $branchcode,
671
        status => 'R',
672
        itemnumber => $item1->itemnumber,
673
        expirationdate => undef,
674
        item_level_recall => 1
675
    })->store;
676
    $recall2 = Koha::Recall->new({
677
        borrowernumber => $patron1->borrowernumber,
678
        recalldate => Koha::DateUtils::dt_from_string,
679
        biblionumber => $biblio->biblionumber,
680
        branchcode => $branchcode,
681
        status => 'R',
682
        itemnumber => undef,
683
        expirationdate => undef,
684
        item_level_recall => 0
685
    })->store;
686
    $recall2->set_waiting({ item => $item1 });
687
688
    # return a waiting recall
689
    my $check_recall = $item1->check_recalls;
690
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
691
692
    $recall2->revert_waiting;
693
694
    # return recall based on recalldate
695
    $check_recall = $item1->check_recalls;
696
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
697
698
    $recall1->set_cancelled;
699
700
    # return a biblio-level recall
701
    $check_recall = $item1->check_recalls;
702
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
703
704
    $recall2->set_cancelled;
705
};
(-)a/t/db_dependent/Koha/Patron.t (+56 lines)
Lines 365-367 subtest 'is_superlibrarian() tests' => sub { Link Here
365
365
366
    $schema->storage->txn_rollback;
366
    $schema->storage->txn_rollback;
367
};
367
};
368
369
subtest 'recalls() tests' => sub {
370
371
    plan tests => 2;
372
373
    $schema->storage->txn_begin;
374
375
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
376
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
377
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
378
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
379
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
380
381
    Koha::Recall->new({
382
        biblionumber => $biblio1->biblionumber,
383
        borrowernumber => $patron->borrowernumber,
384
        itemnumber => $item1->itemnumber,
385
        branchcode => $patron->branchcode,
386
        recalldate => dt_from_string,
387
        status => 'R',
388
        item_level_recall => 1,
389
    })->store;
390
    Koha::Recall->new({
391
        biblionumber => $biblio2->biblionumber,
392
        borrowernumber => $patron->borrowernumber,
393
        itemnumber => $item2->itemnumber,
394
        branchcode => $patron->branchcode,
395
        recalldate => dt_from_string,
396
        status => 'R',
397
        item_level_recall => 1,
398
    })->store;
399
    Koha::Recall->new({
400
        biblionumber => $biblio1->biblionumber,
401
        borrowernumber => $patron->borrowernumber,
402
        itemnumber => undef,
403
        branchcode => $patron->branchcode,
404
        recalldate => dt_from_string,
405
        status => 'R',
406
        item_level_recall => 0,
407
    })->store;
408
    my $recall = Koha::Recall->new({
409
        biblionumber => $biblio1->biblionumber,
410
        borrowernumber => $patron->borrowernumber,
411
        itemnumber => undef,
412
        branchcode => $patron->branchcode,
413
        recalldate => dt_from_string,
414
        status => 'R',
415
        item_level_recall => 0,
416
    })->store;
417
    $recall->set_cancelled;
418
419
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
420
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
421
422
    $schema->storage->txn_rollback;
423
};
(-)a/t/db_dependent/Stats.t (-1 / +1 lines)
Lines 55-61 $return_error = $@; Link Here
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
56
56
57
# returns undef and croaks if mandatory params are missing
57
# returns undef and croaks if mandatory params are missing
58
my @allowed_circulation_types = qw (renew issue localuse return);
58
my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout recall);
59
my @allowed_accounts_types = qw (writeoff payment);
59
my @allowed_accounts_types = qw (writeoff payment);
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
(-)a/t/db_dependent/XSLT.t (-3 / +12 lines)
Lines 34-40 my $builder = t::lib::TestBuilder->new; Link Here
34
$schema->storage->txn_begin;
34
$schema->storage->txn_begin;
35
35
36
subtest 'buildKohaItemsNamespace status tests' => sub {
36
subtest 'buildKohaItemsNamespace status tests' => sub {
37
    plan tests => 13;
37
    plan tests => 14;
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
38
    my $itype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
39
    my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
40
    my $item  = $builder->build_sample_item({ itype => $itype->itemtype });
Lines 103-109 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
103
        }
103
        }
104
    });
104
    });
105
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
105
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
106
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit");
106
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (holds)");
107
    $hold->cancel;
107
108
108
    $builder->build({ source => "TmpHoldsqueue", value => {
109
    $builder->build({ source => "TmpHoldsqueue", value => {
109
        itemnumber => $item->itemnumber
110
        itemnumber => $item->itemnumber
Lines 112-117 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
112
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
113
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
113
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
114
    like($xml,qr{<status>Pending hold</status>},"Pending status takes precedence over all");
114
115
116
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
117
        biblionumber    => $item->biblionumber,
118
        itemnumber      => $item->itemnumber,
119
        branchcode      => $item->holdingbranch,
120
        status          => 'R',
121
    }});
122
    $recall->set_waiting;
123
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
124
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
115
125
116
};
126
};
117
127
118
- 

Return to bug 19532