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

(-)a/C4/Circulation.pm (-2 / +119 lines)
Lines 305-310 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
305
305
306
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
306
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
307
308
=item C<RecallPlacedAtHoldingBranch>
309
310
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.
311
312
=item C<RecallFound>
313
314
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
315
308
=back
316
=back
309
317
310
=back
318
=back
Lines 378-383 sub transferbook { Link Here
378
        $dotransfer = 1;
386
        $dotransfer = 1;
379
    }
387
    }
380
388
389
    # find recall
390
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
391
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
392
        # do a transfer if the recall branch is different to the item holding branch
393
        if ( $recall->branchcode eq $fbr ) {
394
            $dotransfer = 0;
395
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
396
        } else {
397
            $dotransfer = 1;
398
            $messages->{'RecallFound'} = $recall;
399
        }
400
    }
401
381
    #actually do the transfer....
402
    #actually do the transfer....
382
    if ($dotransfer) {
403
    if ($dotransfer) {
383
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
404
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 723-728 sticky due date is invalid or due date in the past Link Here
723
744
724
if the borrower borrows to much things
745
if the borrower borrows to much things
725
746
747
=head3 RECALLED
748
749
recalled by someone else
750
726
=cut
751
=cut
727
752
728
sub CanBookBeIssued {
753
sub CanBookBeIssued {
Lines 1081-1087 sub CanBookBeIssued { Link Here
1081
        }
1106
        }
1082
    }
1107
    }
1083
1108
1084
    unless ( $ignore_reserves ) {
1109
    my $recall;
1110
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1111
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1112
    # Don't look at recalls that are in transit
1113
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1114
        my @recalls = $biblio->recalls;
1115
1116
        foreach my $r ( @recalls ) {
1117
            if ( $r->itemnumber and
1118
                $r->itemnumber == $item_object->itemnumber and
1119
                $r->borrowernumber == $patron->borrowernumber and
1120
                $r->waiting ) {
1121
                $messages{RECALLED} = $r->recall_id;
1122
                $recall = $r;
1123
                # this item is already waiting for this borrower and the recall can be fulfilled
1124
                last;
1125
            }
1126
            elsif ( $r->itemnumber and
1127
                $r->itemnumber == $item_object->itemnumber and
1128
                $r->in_transit ) {
1129
                # recalled item is in transit
1130
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1131
            }
1132
            elsif ( $r->item_level_recall and
1133
                $r->itemnumber == $item_object->itemnumber and
1134
                $r->borrowernumber != $patron->borrowernumber and
1135
                !$r->in_transit ) {
1136
                # this specific item has been recalled by a different patron
1137
                $needsconfirmation{RECALLED} = $r;
1138
                $recall = $r;
1139
                last;
1140
            }
1141
            elsif ( !$r->item_level_recall and
1142
                $r->borrowernumber != $patron->borrowernumber and
1143
                !$r->in_transit ) {
1144
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1145
                $needsconfirmation{RECALLED} = $r;
1146
                $recall = $r;
1147
                last;
1148
            }
1149
        }
1150
    }
1151
1152
    unless ( $ignore_reserves and defined $recall ) {
1085
        # See if the item is on reserve.
1153
        # See if the item is on reserve.
1086
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1154
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1087
        if ($restype) {
1155
        if ($restype) {
Lines 1360-1365 AddIssue does the following things : Link Here
1360
          * RESERVE PLACED ?
1428
          * RESERVE PLACED ?
1361
              - fill reserve if reserve to this patron
1429
              - fill reserve if reserve to this patron
1362
              - cancel reserve or not, otherwise
1430
              - cancel reserve or not, otherwise
1431
          * RECALL PLACED ?
1432
              - fill recall if recall to this patron
1433
              - cancel recall or not
1434
              - revert recall's waiting status or not
1363
          * TRANSFERT PENDING ?
1435
          * TRANSFERT PENDING ?
1364
              - complete the transfert
1436
              - complete the transfert
1365
          * ISSUE THE BOOK
1437
          * ISSUE THE BOOK
Lines 1374-1379 sub AddIssue { Link Here
1374
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1446
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1375
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1447
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1376
    my $auto_renew = $params && $params->{auto_renew};
1448
    my $auto_renew = $params && $params->{auto_renew};
1449
    my $cancel_recall = $params && $params->{cancel_recall};
1450
    my $recall_id = $params && $params->{recall_id};
1377
    my $dbh          = C4::Context->dbh;
1451
    my $dbh          = C4::Context->dbh;
1378
    my $barcodecheck = CheckValidBarcode($barcode);
1452
    my $barcodecheck = CheckValidBarcode($barcode);
1379
1453
Lines 1445-1450 sub AddIssue { Link Here
1445
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1519
                AddReturn( $item_object->barcode, C4::Context->userenv->{'branch'} );
1446
            }
1520
            }
1447
1521
1522
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1523
1448
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1524
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1449
1525
1450
            # Starting process for transfer job (checking transfert and validate it if we have one)
1526
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1852-1857 Value 1 if return is successful. Link Here
1852
1928
1853
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1929
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1854
1930
1931
=item C<RecallFound>
1932
1933
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
1934
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
1935
branchcode.
1936
1937
=item C<TransferredRecall>
1938
1939
This item has been transferred to this branch to fill a recall. The recall object is returned.
1940
1855
=back
1941
=back
1856
1942
1857
C<$iteminformation> is a reference-to-hash, giving information about the
1943
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2079-2084 sub AddReturn { Link Here
2079
        }
2165
        }
2080
    }
2166
    }
2081
2167
2168
    # find recalls...
2169
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2170
    my $recall = undef;
2171
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2172
    if ( defined $recall ) {
2173
        $messages->{RecallFound} = $recall;
2174
        if ( $recall->branchcode ne $branch ) {
2175
            $messages->{RecallNeedsTransfer} = $branch;
2176
        }
2177
    }
2178
2082
    # find reserves.....
2179
    # find reserves.....
2083
    # launch the Checkreserves routine to find any holds
2180
    # launch the Checkreserves routine to find any holds
2084
    my ($resfound, $resrec);
2181
    my ($resfound, $resrec);
Lines 2137-2144 sub AddReturn { Link Here
2137
        $request->status('RET') if $request;
2234
        $request->status('RET') if $request;
2138
    }
2235
    }
2139
2236
2237
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2238
    if ( $transfer_recall and
2239
         $transfer_recall->branchcode eq $branch and
2240
         C4::Context->preference('UseRecalls') ) {
2241
        $messages->{TransferredRecall} = $transfer_recall;
2242
    }
2243
2140
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2244
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2141
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch) ){
2245
    if ($validTransfer && !$is_in_rotating_collection && ($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $returnbranch)
2246
 and not $messages->{'WrongTransfer'} and not $messages->{TransferredRecall} and not $messages->{RecallNeedsTransfer} ){
2142
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2247
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2143
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2248
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2144
            (C4::Context->preference("UseBranchTransferLimits") and
2249
            (C4::Context->preference("UseBranchTransferLimits") and
Lines 2765-2770 sub CanBookBeRenewed { Link Here
2765
        }
2870
        }
2766
    }
2871
    }
2767
2872
2873
    my $recall = undef;
2874
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2875
    if ( defined $recall ) {
2876
        if ( $recall->item_level_recall ) {
2877
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2878
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2879
        } else {
2880
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2881
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2882
        }
2883
    }
2884
2768
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2885
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2769
2886
2770
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2887
    # 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 353-358 sub CanBookBeReserved{ Link Here
353
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
353
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
354
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
354
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
355
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
355
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
356
         { status => recall }, if the borrower has already placed a recall on this item
356
357
357
=cut
358
=cut
358
359
Lines 386-391 sub CanItemBeReserved { Link Here
386
    return { status =>'itemAlreadyOnHold' }
387
    return { status =>'itemAlreadyOnHold' }
387
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
388
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
388
389
390
    # check if a recall exists on this item from this borrower
391
    return { status => 'recall' }
392
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
393
389
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
394
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
390
395
391
    my $querycount = q{
396
    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 816-821 sub to_api_mapping { Link Here
816
    };
816
    };
817
}
817
}
818
818
819
=head3 recalls
820
821
    my @recalls = $biblio->recalls;
822
823
Return all active recalls attached to this biblio, sorted by oldest first
824
825
=cut
826
827
sub recalls {
828
    my ( $self ) = @_;
829
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
830
    return @recalls_rs;
831
}
832
833
=head3 can_be_recalled
834
835
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
836
837
Does biblio-level checks and returns the items attached to this biblio that are available for recall
838
839
=cut
840
841
sub can_be_recalled {
842
    my ( $self, $params ) = @_;
843
844
    return 0 if !( C4::Context->preference('UseRecalls') );
845
846
    my $patron = $params->{patron};
847
848
    my $branchcode = C4::Context->userenv->{'branch'};
849
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
850
        $branchcode = $patron->branchcode;
851
    }
852
853
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
854
855
    # if there are no available items at all, no recall can be placed
856
    return 0 if ( scalar @all_items == 0 );
857
858
    my @itemtypes;
859
    my @itemnumbers;
860
    my @items;
861
    foreach my $item ( @all_items ) {
862
        if ( $item->can_be_recalled({ patron => $patron }) ) {
863
            push( @itemtypes, $item->effective_itemtype );
864
            push( @itemnumbers, $item->itemnumber );
865
            push( @items, $item );
866
        }
867
    }
868
869
    # if there are no recallable items, no recall can be placed
870
    return 0 if ( scalar @items == 0 );
871
872
    # Check the circulation rule for each relevant itemtype for this biblio
873
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
874
    foreach my $itemtype ( @itemtypes ) {
875
        my $rule = Koha::CirculationRules->get_effective_rules({
876
            branchcode => $branchcode,
877
            categorycode => $patron ? $patron->categorycode : undef,
878
            itemtype => $itemtype,
879
            rules => [
880
                'recalls_allowed',
881
                'recalls_per_record',
882
                'on_shelf_recalls',
883
            ],
884
        });
885
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
886
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
887
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
888
    }
889
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
890
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
891
    my %on_shelf_recalls_count = ();
892
    foreach my $count ( @on_shelf_recalls ) {
893
        $on_shelf_recalls_count{$count}++;
894
    }
895
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
896
897
    # check recalls allowed has been set and is not zero
898
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
899
900
    if ( $patron ) {
901
        # check borrower has not reached open recalls allowed limit
902
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
903
904
        # check borrower has not reached open recalls allowed per record limit
905
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
906
907
        # check if any of the items under this biblio are already checked out by this borrower
908
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
909
    }
910
911
    # check item availability
912
    my $checked_out_count = 0;
913
    foreach (@items) {
914
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
915
    }
916
917
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
918
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
919
920
    # can't recall if no items have been checked out
921
    return 0 if ( $checked_out_count == 0 );
922
923
    # can recall
924
    return @items;
925
}
926
819
=head2 Internal methods
927
=head2 Internal methods
820
928
821
=head3 type
929
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 989-994 sub _after_item_action_hooks { Link Here
989
    );
989
    );
990
}
990
}
991
991
992
=head3 recall
993
994
    my $recall = $item->recall;
995
996
Return the relevant recall for this item
997
998
=cut
999
1000
sub recall {
1001
    my ( $self ) = @_;
1002
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1003
    foreach my $recall (@recalls) {
1004
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1005
            return $recall;
1006
        }
1007
    }
1008
    # no item-level recall to return, so return earliest biblio-level
1009
    # FIXME: eventually this will be based on priority
1010
    return $recalls[0];
1011
}
1012
1013
=head3 can_be_recalled
1014
1015
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1016
1017
Does item-level checks and returns if items can be recalled by this borrower
1018
1019
=cut
1020
1021
sub can_be_recalled {
1022
    my ( $self, $params ) = @_;
1023
1024
    return 0 if !( C4::Context->preference('UseRecalls') );
1025
1026
    # check if this item is not for loan, withdrawn or lost
1027
    return 0 if ( $self->notforloan != 0 );
1028
    return 0 if ( $self->itemlost != 0 );
1029
    return 0 if ( $self->withdrawn != 0 );
1030
1031
    # check if this item is not checked out - if not checked out, can't be recalled
1032
    return 0 if ( !defined( $self->checkout ) );
1033
1034
    my $patron = $params->{patron};
1035
1036
    my $branchcode = C4::Context->userenv->{'branch'};
1037
    if ( $patron ) {
1038
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1039
    }
1040
1041
    # Check the circulation rule for each relevant itemtype for this item
1042
    my $rule = Koha::CirculationRules->get_effective_rules({
1043
        branchcode => $branchcode,
1044
        categorycode => $patron ? $patron->categorycode : undef,
1045
        itemtype => $self->effective_itemtype,
1046
        rules => [
1047
            'recalls_allowed',
1048
            'recalls_per_record',
1049
            'on_shelf_recalls',
1050
        ],
1051
    });
1052
1053
    # check recalls allowed has been set and is not zero
1054
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1055
1056
    if ( $patron ) {
1057
        # check borrower has not reached open recalls allowed limit
1058
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1059
1060
        # check borrower has not reach open recalls allowed per record limit
1061
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1062
1063
        # check if this patron has already recalled this item
1064
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1065
1066
        # check if this patron has already checked out this item
1067
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1068
1069
        # check if this patron has already reserved this item
1070
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1071
    }
1072
1073
    # check item availability
1074
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1075
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1076
1077
    # if there are no available items at all, no recall can be placed
1078
    return 0 if ( scalar @items == 0 );
1079
1080
    my $checked_out_count = 0;
1081
    foreach (@items) {
1082
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1083
    }
1084
1085
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1086
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1087
1088
    # can't recall if no items have been checked out
1089
    return 0 if ( $checked_out_count == 0 );
1090
1091
    # can recall
1092
    return 1;
1093
}
1094
1095
=head3 can_be_waiting_recall
1096
1097
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1098
1099
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1100
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1101
1102
=cut
1103
1104
sub can_be_waiting_recall {
1105
    my ( $self ) = @_;
1106
1107
    return 0 if !( C4::Context->preference('UseRecalls') );
1108
1109
    # check if this item is not for loan, withdrawn or lost
1110
    return 0 if ( $self->notforloan != 0 );
1111
    return 0 if ( $self->itemlost != 0 );
1112
    return 0 if ( $self->withdrawn != 0 );
1113
1114
    my $branchcode = $self->holdingbranch;
1115
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1116
        $branchcode = C4::Context->userenv->{'branch'};
1117
    } else {
1118
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1119
    }
1120
1121
    # Check the circulation rule for each relevant itemtype for this item
1122
    my $rule = Koha::CirculationRules->get_effective_rules({
1123
        branchcode => $branchcode,
1124
        categorycode => undef,
1125
        itemtype => $self->effective_itemtype,
1126
        rules => [
1127
            'recalls_allowed',
1128
        ],
1129
    });
1130
1131
    # check recalls allowed has been set and is not zero
1132
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1133
1134
    # can recall
1135
    return 1;
1136
}
1137
1138
=head3 check_recalls
1139
1140
    my $recall = $item->check_recalls;
1141
1142
Get the most relevant recall for this item.
1143
1144
=cut
1145
1146
sub check_recalls {
1147
    my ( $self ) = @_;
1148
1149
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1150
1151
    my $recall;
1152
    # iterate through relevant recalls to find the best one.
1153
    # if we come across a waiting recall, use this one.
1154
    # 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.
1155
    foreach my $r ( @recalls ) {
1156
        if ( $r->waiting ) {
1157
            $recall = $r;
1158
            last;
1159
        }
1160
    }
1161
    unless ( defined $recall ) {
1162
        $recall = $recalls[0];
1163
    }
1164
1165
    return $recall;
1166
}
1167
992
=head3 _type
1168
=head3 _type
993
1169
994
=cut
1170
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1725-1730 sub to_api_mapping { Link Here
1725
    };
1725
    };
1726
}
1726
}
1727
1727
1728
=head3 recalls
1729
1730
    my $recalls = $patron->recalls;
1731
1732
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1733
1734
Return the patron's active recalls - total, or on a specific biblio
1735
1736
=cut
1737
1738
sub recalls {
1739
    my ( $self, $params ) = @_;
1740
1741
    my $biblionumber = $params->{biblionumber};
1742
1743
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1744
1745
    if ( $biblionumber ) {
1746
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1747
    }
1748
1749
    return $recalls_rs;
1750
}
1751
1728
=head2 Internal methods
1752
=head2 Internal methods
1729
1753
1730
=head3 _type
1754
=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 => 49;
21
use Test::More tests => 52;
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 1257-1262 subtest "CanBookBeRenewed tests" => sub { Link Here
1257
            $item_3->itemcallnumber || '' ),
1257
            $item_3->itemcallnumber || '' ),
1258
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1258
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1259
    );
1259
    );
1260
1261
    # Recalls
1262
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1263
    Koha::CirculationRules->set_rules({
1264
        categorycode => undef,
1265
        branchcode => undef,
1266
        itemtype => undef,
1267
        rules => {
1268
            recalls_allowed => 10,
1269
            renewalsallowed => 5,
1270
        },
1271
    });
1272
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1273
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1274
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1275
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1276
1277
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1278
1279
    # item-level and this item: renewal not allowed
1280
    my $recall = Koha::Recall->new({
1281
        biblionumber => $recall_item1->biblionumber,
1282
        itemnumber => $recall_item1->itemnumber,
1283
        borrowernumber => $recall_borrower->borrowernumber,
1284
        branchcode => $recall_borrower->branchcode,
1285
        item_level_recall => 1,
1286
        status => 'R',
1287
    })->store;
1288
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1289
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1290
    $recall->set_cancelled;
1291
1292
    # biblio-level requested recall: renewal not allowed
1293
    $recall = Koha::Recall->new({
1294
        biblionumber => $recall_item1->biblionumber,
1295
        itemnumber => undef,
1296
        borrowernumber => $recall_borrower->borrowernumber,
1297
        branchcode => $recall_borrower->branchcode,
1298
        item_level_recall => 0,
1299
        status => 'R',
1300
    })->store;
1301
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1302
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1303
    $recall->set_cancelled;
1304
1305
    # item-level and not this item: renewal allowed
1306
    $recall = Koha::Recall->new({
1307
        biblionumber => $recall_item2->biblionumber,
1308
        itemnumber => $recall_item2->itemnumber,
1309
        borrowernumber => $recall_borrower->borrowernumber,
1310
        branchcode => $recall_borrower->branchcode,
1311
        item_level_recall => 1,
1312
        status => 'R',
1313
    })->store;
1314
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1315
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1316
    $recall->set_cancelled;
1317
1318
    # biblio-level waiting recall: renewal allowed
1319
    $recall = Koha::Recall->new({
1320
        biblionumber => $recall_item1->biblionumber,
1321
        itemnumber => undef,
1322
        borrowernumber => $recall_borrower->borrowernumber,
1323
        branchcode => $recall_borrower->branchcode,
1324
        item_level_recall => 0,
1325
        status => 'R',
1326
    })->store;
1327
    $recall->set_waiting({ item => $recall_item1 });
1328
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1329
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1330
    $recall->set_cancelled;
1260
};
1331
};
1261
1332
1262
subtest "GetUpcomingDueIssues" => sub {
1333
subtest "GetUpcomingDueIssues" => sub {
Lines 1737-1742 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1737
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1808
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1738
};
1809
};
1739
1810
1811
subtest 'AddIssue | recalls' => sub {
1812
    plan tests => 3;
1813
1814
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1815
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1816
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1817
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1818
    my $item = $builder->build_sample_item;
1819
    Koha::CirculationRules->set_rules({
1820
        branchcode => undef,
1821
        itemtype => undef,
1822
        categorycode => undef,
1823
        rules => {
1824
            recalls_allowed => 10,
1825
        },
1826
    });
1827
1828
    # checking out item that they have recalled
1829
    my $recall1 = Koha::Recall->new({
1830
        borrowernumber => $patron1->borrowernumber,
1831
        biblionumber => $item->biblionumber,
1832
        itemnumber => $item->itemnumber,
1833
        item_level_recall => 1,
1834
        branchcode => $patron1->branchcode,
1835
        status => 'R',
1836
    })->store;
1837
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
1838
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
1839
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
1840
    AddReturn( $item->barcode, $item->homebranch );
1841
1842
    # this item is has a recall request. cancel recall
1843
    my $recall2 = Koha::Recall->new({
1844
        borrowernumber => $patron2->borrowernumber,
1845
        biblionumber => $item->biblionumber,
1846
        itemnumber => $item->itemnumber,
1847
        item_level_recall => 1,
1848
        branchcode => $patron2->branchcode,
1849
        status => 'R',
1850
    })->store;
1851
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
1852
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
1853
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
1854
    AddReturn( $item->barcode, $item->homebranch );
1855
1856
    # this item is waiting to fulfill a recall. revert recall
1857
    my $recall3 = Koha::Recall->new({
1858
        borrowernumber => $patron2->borrowernumber,
1859
        biblionumber => $item->biblionumber,
1860
        itemnumber => $item->itemnumber,
1861
        item_level_recall => 1,
1862
        branchcode => $patron2->branchcode,
1863
        status => 'R',
1864
    })->store;
1865
    $recall3->set_waiting;
1866
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
1867
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
1868
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
1869
    AddReturn( $item->barcode, $item->homebranch );
1870
};
1871
1872
1740
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1873
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1741
    plan tests => 8;
1874
    plan tests => 8;
1742
1875
Lines 3157-3162 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3157
    # TODO test with AllowNotForLoanOverride = 1
3290
    # TODO test with AllowNotForLoanOverride = 1
3158
};
3291
};
3159
3292
3293
subtest 'CanBookBeIssued | recalls' => sub {
3294
    plan tests => 3;
3295
3296
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3297
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3298
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3299
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3300
    my $item = $builder->build_sample_item;
3301
    Koha::CirculationRules->set_rules({
3302
        branchcode => undef,
3303
        itemtype => undef,
3304
        categorycode => undef,
3305
        rules => {
3306
            recalls_allowed => 10,
3307
        },
3308
    });
3309
3310
    # item-level recall
3311
    my $recall = Koha::Recall->new({
3312
        borrowernumber => $patron1->borrowernumber,
3313
        biblionumber => $item->biblionumber,
3314
        itemnumber => $item->itemnumber,
3315
        item_level_recall => 1,
3316
        branchcode => $patron1->branchcode,
3317
        status => 'R',
3318
    })->store;
3319
3320
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3321
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3322
3323
    $recall->set_cancelled;
3324
3325
    # biblio-level recall
3326
    $recall = Koha::Recall->new({
3327
        borrowernumber => $patron1->borrowernumber,
3328
        biblionumber => $item->biblionumber,
3329
        itemnumber => undef,
3330
        item_level_recall => 0,
3331
        branchcode => $patron1->branchcode,
3332
        status => 'R',
3333
    })->store;
3334
3335
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3336
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
3337
3338
    $recall->set_cancelled;
3339
3340
    # biblio-level recall
3341
    $recall = Koha::Recall->new({
3342
        borrowernumber => $patron1->borrowernumber,
3343
        biblionumber => $item->biblionumber,
3344
        itemnumber => undef,
3345
        item_level_recall => 0,
3346
        branchcode => $patron1->branchcode,
3347
        status => 'R',
3348
    })->store;
3349
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
3350
3351
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
3352
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
3353
3354
    $recall->set_cancelled;
3355
};
3356
3160
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3357
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3161
    plan tests => 1;
3358
    plan tests => 1;
3162
3359
Lines 3172-3177 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3172
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3369
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3173
};
3370
};
3174
3371
3372
subtest 'AddReturn | recalls' => sub {
3373
    plan tests => 3;
3374
3375
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3376
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3377
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3378
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3379
    my $item1 = $builder->build_sample_item;
3380
    Koha::CirculationRules->set_rules({
3381
        branchcode => undef,
3382
        itemtype => undef,
3383
        categorycode => undef,
3384
        rules => {
3385
            recalls_allowed => 10,
3386
        },
3387
    });
3388
3389
    # this item can fill a recall with pickup at this branch
3390
    AddIssue( $patron1->unblessed, $item1->barcode );
3391
    my $recall1 = Koha::Recall->new({
3392
        borrowernumber => $patron2->borrowernumber,
3393
        biblionumber => $item1->biblionumber,
3394
        itemnumber => $item1->itemnumber,
3395
        item_level_recall => 1,
3396
        branchcode => $item1->homebranch,
3397
        status => 'R',
3398
    })->store;
3399
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3400
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
3401
    $recall1->set_cancelled;
3402
3403
    # this item can fill a recall but needs transfer
3404
    AddIssue( $patron1->unblessed, $item1->barcode );
3405
    $recall1 = Koha::Recall->new({
3406
        borrowernumber => $patron2->borrowernumber,
3407
        biblionumber => $item1->biblionumber,
3408
        itemnumber => $item1->itemnumber,
3409
        item_level_recall => 1,
3410
        branchcode => $patron2->branchcode,
3411
        status => 'R',
3412
    })->store;
3413
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
3414
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
3415
    $recall1->set_cancelled;
3416
3417
    # this item is already in transit, do not ask to transfer
3418
    AddIssue( $patron1->unblessed, $item1->barcode );
3419
    $recall1 = Koha::Recall->new({
3420
        borrowernumber => $patron2->borrowernumber,
3421
        biblionumber => $item1->biblionumber,
3422
        itemnumber => $item1->itemnumber,
3423
        item_level_recall => 1,
3424
        branchcode => $patron2->branchcode,
3425
        status => 'R',
3426
    })->store;
3427
    $recall1->start_transfer;
3428
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
3429
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
3430
    $recall1->set_cancelled;
3431
};
3175
3432
3176
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3433
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3177
3434
(-)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 184-194 subtest 'pickup_locations' => sub { Link Here
184
184
185
    # Cleanup database
185
    # Cleanup database
186
    Koha::Holds->search->delete;
186
    Koha::Holds->search->delete;
187
    $dbh->do('DELETE FROM issues');
187
    Koha::Patrons->search->delete;
188
    Koha::Patrons->search->delete;
188
    Koha::Items->search->delete;
189
    Koha::Items->search->delete;
189
    Koha::Libraries->search->delete;
190
    Koha::Libraries->search->delete;
190
    Koha::CirculationRules->search->delete;
191
    Koha::CirculationRules->search->delete;
191
    $dbh->do('DELETE FROM issues');
192
    Koha::CirculationRules->set_rules(
192
    Koha::CirculationRules->set_rules(
193
        {
193
        {
194
            categorycode => undef,
194
            categorycode => undef,
Lines 549-551 subtest 'subscriptions() tests' => sub { Link Here
549
549
550
    $schema->storage->txn_rollback;
550
    $schema->storage->txn_rollback;
551
};
551
};
552
553
subtest 'Recalls tests' => sub {
554
555
    plan tests => 12;
556
557
    $schema->storage->txn_begin;
558
    my $item1 = $builder->build_sample_item;
559
    my $biblio = $item1->biblio;
560
    my $branchcode = $item1->holdingbranch;
561
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
562
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
563
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
564
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
565
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
566
567
    my $recall1 = Koha::Recall->new({
568
        borrowernumber => $patron1->borrowernumber,
569
        recalldate => Koha::DateUtils::dt_from_string,
570
        biblionumber => $biblio->biblionumber,
571
        branchcode => $branchcode,
572
        status => 'R',
573
        itemnumber => $item1->itemnumber,
574
        expirationdate => undef,
575
        item_level_recall => 1
576
    })->store;
577
    my $recall2 = Koha::Recall->new({
578
        borrowernumber => $patron2->borrowernumber,
579
        recalldate => Koha::DateUtils::dt_from_string,
580
        biblionumber => $biblio->biblionumber,
581
        branchcode => $branchcode,
582
        status => 'R',
583
        itemnumber => undef,
584
        expirationdate => undef,
585
        item_level_recall => 0
586
    })->store;
587
    my $recall3 = Koha::Recall->new({
588
        borrowernumber => $patron3->borrowernumber,
589
        recalldate => Koha::DateUtils::dt_from_string,
590
        biblionumber => $biblio->biblionumber,
591
        branchcode => $branchcode,
592
        status => 'R',
593
        itemnumber => $item1->itemnumber,
594
        expirationdate => undef,
595
        item_level_recall => 1
596
    })->store;
597
598
    my $recalls_count = scalar $biblio->recalls;
599
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
600
601
    $recall1->set_cancelled;
602
    $recall2->set_expired({ interface => 'COMMANDLINE' });
603
604
    $recalls_count = scalar $biblio->recalls;
605
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
606
607
    t::lib::Mocks::mock_preference('UseRecalls', 0);
608
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
609
610
    t::lib::Mocks::mock_preference("UseRecalls", 1);
611
    $item1->update({ notforloan => 1 });
612
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
613
614
    $item1->update({ notforloan => 0 });
615
    Koha::CirculationRules->set_rules({
616
        branchcode => $branchcode,
617
        categorycode => $patron1->categorycode,
618
        itemtype => $item1->effective_itemtype,
619
        rules => {
620
            recalls_allowed => 0,
621
            recalls_per_record => 1,
622
            on_shelf_recalls => 'all',
623
        },
624
    });
625
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
626
627
    Koha::CirculationRules->set_rules({
628
        branchcode => $branchcode,
629
        categorycode => $patron1->categorycode,
630
        itemtype => $item1->effective_itemtype,
631
        rules => {
632
            recalls_allowed => 1,
633
            recalls_per_record => 1,
634
            on_shelf_recalls => 'all',
635
        },
636
    });
637
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
638
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
639
640
    $recall1->set_cancelled;
641
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
642
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
643
644
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
645
646
    Koha::CirculationRules->set_rules({
647
        branchcode => $branchcode,
648
        categorycode => $patron1->categorycode,
649
        itemtype => $item1->effective_itemtype,
650
        rules => {
651
            recalls_allowed => 1,
652
            recalls_per_record => 1,
653
            on_shelf_recalls => 'any',
654
        },
655
    });
656
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
657
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
658
659
    $recall2->set_cancelled;
660
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
661
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
662
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
663
664
    $item1->update({ withdrawn => 1 });
665
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
666
667
    $schema->storage->txn_rollback;
668
};
(-)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 (-1 / +57 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 5;
22
use Test::More tests => 6;
23
use Test::Exception;
23
use Test::Exception;
24
24
25
use Koha::Database;
25
use Koha::Database;
Lines 241-243 subtest 'is_superlibrarian() tests' => sub { Link Here
241
241
242
    $schema->storage->txn_rollback;
242
    $schema->storage->txn_rollback;
243
};
243
};
244
245
subtest 'recalls() tests' => sub {
246
247
    plan tests => 2;
248
249
    $schema->storage->txn_begin;
250
251
    my $patron = $builder->build_object({ class => 'Koha::Patrons' });
252
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
253
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
254
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
255
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
256
257
    Koha::Recall->new({
258
        biblionumber => $biblio1->biblionumber,
259
        borrowernumber => $patron->borrowernumber,
260
        itemnumber => $item1->itemnumber,
261
        branchcode => $patron->branchcode,
262
        recalldate => dt_from_string,
263
        status => 'R',
264
        item_level_recall => 1,
265
    })->store;
266
    Koha::Recall->new({
267
        biblionumber => $biblio2->biblionumber,
268
        borrowernumber => $patron->borrowernumber,
269
        itemnumber => $item2->itemnumber,
270
        branchcode => $patron->branchcode,
271
        recalldate => dt_from_string,
272
        status => 'R',
273
        item_level_recall => 1,
274
    })->store;
275
    Koha::Recall->new({
276
        biblionumber => $biblio1->biblionumber,
277
        borrowernumber => $patron->borrowernumber,
278
        itemnumber => undef,
279
        branchcode => $patron->branchcode,
280
        recalldate => dt_from_string,
281
        status => 'R',
282
        item_level_recall => 0,
283
    })->store;
284
    my $recall = Koha::Recall->new({
285
        biblionumber => $biblio1->biblionumber,
286
        borrowernumber => $patron->borrowernumber,
287
        itemnumber => undef,
288
        branchcode => $patron->branchcode,
289
        recalldate => dt_from_string,
290
        status => 'R',
291
        item_level_recall => 0,
292
    })->store;
293
    $recall->set_cancelled;
294
295
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
296
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
297
298
    $schema->storage->txn_rollback;
299
};
(-)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