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 1911-1916 Value 1 if return is successful. Link Here
1911
1987
1912
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1988
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1913
1989
1990
=item C<RecallFound>
1991
1992
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
1993
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
1994
branchcode.
1995
1996
=item C<TransferredRecall>
1997
1998
This item has been transferred to this branch to fill a recall. The recall object is returned.
1999
1914
=back
2000
=back
1915
2001
1916
C<$iteminformation> is a reference-to-hash, giving information about the
2002
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2167-2172 sub AddReturn { Link Here
2167
        }
2253
        }
2168
    }
2254
    }
2169
2255
2256
    # find recalls...
2257
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2258
    my $recall = undef;
2259
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2260
    if ( defined $recall ) {
2261
        $messages->{RecallFound} = $recall;
2262
        if ( $recall->branchcode ne $branch ) {
2263
            $messages->{RecallNeedsTransfer} = $branch;
2264
        }
2265
    }
2266
2170
    # find reserves.....
2267
    # find reserves.....
2171
    # launch the Checkreserves routine to find any holds
2268
    # launch the Checkreserves routine to find any holds
2172
    my ($resfound, $resrec);
2269
    my ($resfound, $resrec);
Lines 2225-2232 sub AddReturn { Link Here
2225
        $request->status('RET') if $request;
2322
        $request->status('RET') if $request;
2226
    }
2323
    }
2227
2324
2325
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2326
    if ( $transfer_recall and
2327
         $transfer_recall->branchcode eq $branch and
2328
         C4::Context->preference('UseRecalls') ) {
2329
        $messages->{TransferredRecall} = $transfer_recall;
2330
    }
2331
2228
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2332
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2229
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
2333
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch)
2334
 and not $messages->{'WrongTransfer'} and not $messages->{TransferredRecall} and not $messages->{RecallNeedsTransfer} ){
2230
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2335
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2231
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2336
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2232
            (C4::Context->preference("UseBranchTransferLimits") and
2337
            (C4::Context->preference("UseBranchTransferLimits") and
Lines 2855-2860 sub CanBookBeRenewed { Link Here
2855
        }
2960
        }
2856
    }
2961
    }
2857
2962
2963
    my $recall = undef;
2964
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2965
    if ( defined $recall ) {
2966
        if ( $recall->item_level_recall ) {
2967
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2968
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2969
        } else {
2970
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2971
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2972
        }
2973
    }
2974
2858
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2975
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2859
2976
2860
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2977
    # 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 1781-1786 sub to_api_mapping { Link Here
1781
    };
1781
    };
1782
}
1782
}
1783
1783
1784
=head3 recalls
1785
1786
    my $recalls = $patron->recalls;
1787
1788
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1789
1790
Return the patron's active recalls - total, or on a specific biblio
1791
1792
=cut
1793
1794
sub recalls {
1795
    my ( $self, $params ) = @_;
1796
1797
    my $biblionumber = $params->{biblionumber};
1798
1799
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1800
1801
    if ( $biblionumber ) {
1802
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1803
    }
1804
1805
    return $recalls_rs;
1806
}
1807
1784
=head2 Internal methods
1808
=head2 Internal methods
1785
1809
1786
=head3 _type
1810
=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 => 50;
21
use Test::More tests => 53;
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 1258-1263 subtest "CanBookBeRenewed tests" => sub { Link Here
1258
            $item_3->itemcallnumber || '' ),
1258
            $item_3->itemcallnumber || '' ),
1259
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1259
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1260
    );
1260
    );
1261
1262
    # Recalls
1263
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1264
    Koha::CirculationRules->set_rules({
1265
        categorycode => undef,
1266
        branchcode => undef,
1267
        itemtype => undef,
1268
        rules => {
1269
            recalls_allowed => 10,
1270
            renewalsallowed => 5,
1271
        },
1272
    });
1273
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1274
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1275
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1276
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1277
1278
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1279
1280
    # item-level and this item: renewal not allowed
1281
    my $recall = Koha::Recall->new({
1282
        biblionumber => $recall_item1->biblionumber,
1283
        itemnumber => $recall_item1->itemnumber,
1284
        borrowernumber => $recall_borrower->borrowernumber,
1285
        branchcode => $recall_borrower->branchcode,
1286
        item_level_recall => 1,
1287
        status => 'R',
1288
    })->store;
1289
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1290
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1291
    $recall->set_cancelled;
1292
1293
    # biblio-level requested recall: renewal not allowed
1294
    $recall = Koha::Recall->new({
1295
        biblionumber => $recall_item1->biblionumber,
1296
        itemnumber => undef,
1297
        borrowernumber => $recall_borrower->borrowernumber,
1298
        branchcode => $recall_borrower->branchcode,
1299
        item_level_recall => 0,
1300
        status => 'R',
1301
    })->store;
1302
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1303
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1304
    $recall->set_cancelled;
1305
1306
    # item-level and not this item: renewal allowed
1307
    $recall = Koha::Recall->new({
1308
        biblionumber => $recall_item2->biblionumber,
1309
        itemnumber => $recall_item2->itemnumber,
1310
        borrowernumber => $recall_borrower->borrowernumber,
1311
        branchcode => $recall_borrower->branchcode,
1312
        item_level_recall => 1,
1313
        status => 'R',
1314
    })->store;
1315
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1316
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1317
    $recall->set_cancelled;
1318
1319
    # biblio-level waiting recall: renewal allowed
1320
    $recall = Koha::Recall->new({
1321
        biblionumber => $recall_item1->biblionumber,
1322
        itemnumber => undef,
1323
        borrowernumber => $recall_borrower->borrowernumber,
1324
        branchcode => $recall_borrower->branchcode,
1325
        item_level_recall => 0,
1326
        status => 'R',
1327
    })->store;
1328
    $recall->set_waiting({ item => $recall_item1 });
1329
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1330
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1331
    $recall->set_cancelled;
1261
};
1332
};
1262
1333
1263
subtest "GetUpcomingDueIssues" => sub {
1334
subtest "GetUpcomingDueIssues" => sub {
Lines 1738-1743 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1738
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1809
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1739
};
1810
};
1740
1811
1812
subtest 'AddIssue | recalls' => sub {
1813
    plan tests => 3;
1814
1815
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1816
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1817
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1818
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1819
    my $item = $builder->build_sample_item;
1820
    Koha::CirculationRules->set_rules({
1821
        branchcode => undef,
1822
        itemtype => undef,
1823
        categorycode => undef,
1824
        rules => {
1825
            recalls_allowed => 10,
1826
        },
1827
    });
1828
1829
    # checking out item that they have recalled
1830
    my $recall1 = Koha::Recall->new({
1831
        borrowernumber => $patron1->borrowernumber,
1832
        biblionumber => $item->biblionumber,
1833
        itemnumber => $item->itemnumber,
1834
        item_level_recall => 1,
1835
        branchcode => $patron1->branchcode,
1836
        status => 'R',
1837
    })->store;
1838
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1839
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1840
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1841
    AddReturn( $item->barcode, $item->homebranch );
1842
1843
    # this item is has a recall request. cancel recall
1844
    my $recall2 = Koha::Recall->new({
1845
        borrowernumber => $patron2->borrowernumber,
1846
        biblionumber => $item->biblionumber,
1847
        itemnumber => $item->itemnumber,
1848
        item_level_recall => 1,
1849
        branchcode => $patron2->branchcode,
1850
        status => 'R',
1851
    })->store;
1852
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1853
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1854
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1855
    AddReturn( $item->barcode, $item->homebranch );
1856
1857
    # this item is waiting to fulfill a recall. revert recall
1858
    my $recall3 = Koha::Recall->new({
1859
        borrowernumber => $patron2->borrowernumber,
1860
        biblionumber => $item->biblionumber,
1861
        itemnumber => $item->itemnumber,
1862
        item_level_recall => 1,
1863
        branchcode => $patron2->branchcode,
1864
        status => 'R',
1865
    })->store;
1866
    $recall3->set_waiting;
1867
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1868
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1869
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1870
    AddReturn( $item->barcode, $item->homebranch );
1871
};
1872
1873
1741
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1874
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1742
    plan tests => 8;
1875
    plan tests => 8;
1743
1876
Lines 3641-3646 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3641
    # TODO test with AllowNotForLoanOverride = 1
3774
    # TODO test with AllowNotForLoanOverride = 1
3642
};
3775
};
3643
3776
3777
subtest 'CanBookBeIssued | recalls' => sub {
3778
    plan tests => 3;
3779
3780
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3781
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3782
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3783
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3784
    my $item = $builder->build_sample_item;
3785
    Koha::CirculationRules->set_rules({
3786
        branchcode => undef,
3787
        itemtype => undef,
3788
        categorycode => undef,
3789
        rules => {
3790
            recalls_allowed => 10,
3791
        },
3792
    });
3793
3794
    # item-level recall
3795
    my $recall = Koha::Recall->new({
3796
        borrowernumber => $patron1->borrowernumber,
3797
        biblionumber => $item->biblionumber,
3798
        itemnumber => $item->itemnumber,
3799
        item_level_recall => 1,
3800
        branchcode => $patron1->branchcode,
3801
        status => 'R',
3802
    })->store;
3803
3804
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3805
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3806
3807
    $recall->set_cancelled;
3808
3809
    # biblio-level recall
3810
    $recall = Koha::Recall->new({
3811
        borrowernumber => $patron1->borrowernumber,
3812
        biblionumber => $item->biblionumber,
3813
        itemnumber => undef,
3814
        item_level_recall => 0,
3815
        branchcode => $patron1->branchcode,
3816
        status => 'R',
3817
    })->store;
3818
3819
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3820
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3821
3822
    $recall->set_cancelled;
3823
3824
    # biblio-level recall
3825
    $recall = Koha::Recall->new({
3826
        borrowernumber => $patron1->borrowernumber,
3827
        biblionumber => $item->biblionumber,
3828
        itemnumber => undef,
3829
        item_level_recall => 0,
3830
        branchcode => $patron1->branchcode,
3831
        status => 'R',
3832
    })->store;
3833
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
3834
3835
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
3836
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
3837
3838
    $recall->set_cancelled;
3839
};
3840
3644
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3841
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3645
    plan tests => 1;
3842
    plan tests => 1;
3646
3843
Lines 3656-3661 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3656
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3853
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3657
};
3854
};
3658
3855
3856
subtest 'AddReturn | recalls' => sub {
3857
    plan tests => 3;
3858
3859
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3860
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3861
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3862
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3863
    my $item1 = $builder->build_sample_item;
3864
    Koha::CirculationRules->set_rules({
3865
        branchcode => undef,
3866
        itemtype => undef,
3867
        categorycode => undef,
3868
        rules => {
3869
            recalls_allowed => 10,
3870
        },
3871
    });
3872
3873
    # this item can fill a recall with pickup at this branch
3874
    AddIssue( $patron1->unblessed, $item1->barcode );
3875
    my $recall1 = Koha::Recall->new({
3876
        borrowernumber => $patron2->borrowernumber,
3877
        biblionumber => $item1->biblionumber,
3878
        itemnumber => $item1->itemnumber,
3879
        item_level_recall => 1,
3880
        branchcode => $item1->homebranch,
3881
        status => 'R',
3882
    })->store;
3883
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3884
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3885
    $recall1->set_cancelled;
3886
3887
    # this item can fill a recall but needs transfer
3888
    AddIssue( $patron1->unblessed, $item1->barcode );
3889
    $recall1 = Koha::Recall->new({
3890
        borrowernumber => $patron2->borrowernumber,
3891
        biblionumber => $item1->biblionumber,
3892
        itemnumber => $item1->itemnumber,
3893
        item_level_recall => 1,
3894
        branchcode => $patron2->branchcode,
3895
        status => 'R',
3896
    })->store;
3897
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3898
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3899
    $recall1->set_cancelled;
3900
3901
    # this item is already in transit, do not ask to transfer
3902
    AddIssue( $patron1->unblessed, $item1->barcode );
3903
    $recall1 = Koha::Recall->new({
3904
        borrowernumber => $patron2->borrowernumber,
3905
        biblionumber => $item1->biblionumber,
3906
        itemnumber => $item1->itemnumber,
3907
        item_level_recall => 1,
3908
        branchcode => $patron2->branchcode,
3909
        status => 'R',
3910
    })->store;
3911
    $recall1->start_transfer;
3912
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3913
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3914
    $recall1->set_cancelled;
3915
};
3659
3916
3660
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3917
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3661
3918
(-)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