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

(-)a/C4/Circulation.pm (-2 / +120 lines)
Lines 299-304 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
299
299
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
301
301
302
=item C<RecallPlacedAtHoldingBranch>
303
304
A recall for this item was found, and the transfer has already been completed as the item's branch matches the recall's pickup branch.
305
306
=item C<RecallFound>
307
308
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
309
302
=back
310
=back
303
311
304
=back
312
=back
Lines 372-377 sub transferbook { Link Here
372
        $dotransfer = 0 unless $ignoreRs;
380
        $dotransfer = 0 unless $ignoreRs;
373
    }
381
    }
374
382
383
    # find recall
384
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
385
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
386
        # do a transfer if the recall branch is different to the item holding branch
387
        if ( $recall->branchcode eq $fbr ) {
388
            $dotransfer = 0;
389
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
390
        } else {
391
            $dotransfer = 1;
392
            $messages->{'RecallFound'} = $recall;
393
        }
394
    }
395
375
    #actually do the transfer....
396
    #actually do the transfer....
376
    if ($dotransfer) {
397
    if ($dotransfer) {
377
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
398
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 722-727 sticky due date is invalid or due date in the past Link Here
722
743
723
if the borrower borrows to much things
744
if the borrower borrows to much things
724
745
746
=head3 RECALLED
747
748
recalled by someone else
749
725
=cut
750
=cut
726
751
727
sub CanBookBeIssued {
752
sub CanBookBeIssued {
Lines 1095-1101 sub CanBookBeIssued { Link Here
1095
        }
1120
        }
1096
    }
1121
    }
1097
1122
1098
    unless ( $ignore_reserves ) {
1123
    my $recall;
1124
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1125
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1126
    # Don't look at recalls that are in transit
1127
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1128
        my @recalls = $biblio->recalls;
1129
1130
        foreach my $r ( @recalls ) {
1131
            if ( $r->itemnumber and
1132
                $r->itemnumber == $item_object->itemnumber and
1133
                $r->borrowernumber == $patron->borrowernumber and
1134
                $r->waiting ) {
1135
                $messages{RECALLED} = $r->recall_id;
1136
                $recall = $r;
1137
                # this item is already waiting for this borrower and the recall can be fulfilled
1138
                last;
1139
            }
1140
            elsif ( $r->itemnumber and
1141
                $r->itemnumber == $item_object->itemnumber and
1142
                $r->in_transit ) {
1143
                # recalled item is in transit
1144
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1145
            }
1146
            elsif ( $r->item_level_recall and
1147
                $r->itemnumber == $item_object->itemnumber and
1148
                $r->borrowernumber != $patron->borrowernumber and
1149
                !$r->in_transit ) {
1150
                # this specific item has been recalled by a different patron
1151
                $needsconfirmation{RECALLED} = $r;
1152
                $recall = $r;
1153
                last;
1154
            }
1155
            elsif ( !$r->item_level_recall and
1156
                $r->borrowernumber != $patron->borrowernumber and
1157
                !$r->in_transit ) {
1158
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1159
                $needsconfirmation{RECALLED} = $r;
1160
                $recall = $r;
1161
                last;
1162
            }
1163
        }
1164
    }
1165
1166
    unless ( $ignore_reserves and defined $recall ) {
1099
        # See if the item is on reserve.
1167
        # See if the item is on reserve.
1100
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1168
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1101
        if ($restype) {
1169
        if ($restype) {
Lines 1409-1414 AddIssue does the following things : Link Here
1409
          * RESERVE PLACED ?
1477
          * RESERVE PLACED ?
1410
              - fill reserve if reserve to this patron
1478
              - fill reserve if reserve to this patron
1411
              - cancel reserve or not, otherwise
1479
              - cancel reserve or not, otherwise
1480
          * RECALL PLACED ?
1481
              - fill recall if recall to this patron
1482
              - cancel recall or not
1483
              - revert recall's waiting status or not
1412
          * TRANSFERT PENDING ?
1484
          * TRANSFERT PENDING ?
1413
              - complete the transfert
1485
              - complete the transfert
1414
          * ISSUE THE BOOK
1486
          * ISSUE THE BOOK
Lines 1423-1428 sub AddIssue { Link Here
1423
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1495
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1424
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1496
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1425
    my $auto_renew = $params && $params->{auto_renew};
1497
    my $auto_renew = $params && $params->{auto_renew};
1498
    my $cancel_recall = $params && $params->{cancel_recall};
1499
    my $recall_id = $params && $params->{recall_id};
1426
    my $dbh          = C4::Context->dbh;
1500
    my $dbh          = C4::Context->dbh;
1427
    my $barcodecheck = CheckValidBarcode($barcode);
1501
    my $barcodecheck = CheckValidBarcode($barcode);
1428
1502
Lines 1496-1501 sub AddIssue { Link Here
1496
                $item_object->discard_changes;
1570
                $item_object->discard_changes;
1497
            }
1571
            }
1498
1572
1573
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1574
1499
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1575
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1500
1576
1501
            # Starting process for transfer job (checking transfert and validate it if we have one)
1577
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1922-1927 Value 1 if return is successful. Link Here
1922
1998
1923
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1999
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1924
2000
2001
=item C<RecallFound>
2002
2003
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2004
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2005
branchcode.
2006
2007
=item C<TransferredRecall>
2008
2009
This item has been transferred to this branch to fill a recall. The recall object is returned.
2010
1925
=back
2011
=back
1926
2012
1927
C<$iteminformation> is a reference-to-hash, giving information about the
2013
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2198-2203 sub AddReturn { Link Here
2198
        }
2284
        }
2199
    }
2285
    }
2200
2286
2287
    # find recalls...
2288
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2289
    my $recall = undef;
2290
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2291
    if ( defined $recall ) {
2292
        $messages->{RecallFound} = $recall;
2293
        if ( $recall->branchcode ne $branch ) {
2294
            $messages->{RecallNeedsTransfer} = $branch;
2295
        }
2296
    }
2297
2201
    # find reserves.....
2298
    # find reserves.....
2202
    # launch the Checkreserves routine to find any holds
2299
    # launch the Checkreserves routine to find any holds
2203
    my ($resfound, $resrec);
2300
    my ($resfound, $resrec);
Lines 2257-2269 sub AddReturn { Link Here
2257
        $request->status('RET') if $request;
2354
        $request->status('RET') if $request;
2258
    }
2355
    }
2259
2356
2357
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2358
    if ( $transfer_recall and
2359
         $transfer_recall->branchcode eq $branch and
2360
         C4::Context->preference('UseRecalls') ) {
2361
        $messages->{TransferredRecall} = $transfer_recall;
2362
    }
2363
2260
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2364
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2261
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2365
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2262
        && ( $doreturn or $messages->{'NotIssued'} )
2366
        && ( $doreturn or $messages->{'NotIssued'} )
2263
        and !$resfound
2367
        and !$resfound
2264
        and ( $branch ne $returnbranch )
2368
        and ( $branch ne $returnbranch )
2265
        and not $messages->{'WrongTransfer'}
2369
        and not $messages->{'WrongTransfer'}
2266
        and not $messages->{'WasTransfered'} )
2370
        and not $messages->{'WasTransfered'}
2371
        and not $messages->{TransferredRecall}
2372
        and not $messages->{RecallNeedsTransfer} )
2267
    {
2373
    {
2268
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2374
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2269
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2375
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
Lines 2784-2789 sub CanBookBeRenewed { Link Here
2784
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2890
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2785
    }
2891
    }
2786
2892
2893
    my $recall = undef;
2894
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2895
    if ( defined $recall ) {
2896
        if ( $recall->item_level_recall ) {
2897
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2898
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2899
        } else {
2900
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2901
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2902
        }
2903
    }
2904
2787
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2905
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2788
2906
2789
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2907
    # 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 375-380 sub CanBookBeReserved{ Link Here
375
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
375
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
376
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
376
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
377
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
377
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
378
         { status => recall }, if the borrower has already placed a recall on this item
378
379
379
=cut
380
=cut
380
381
Lines 420-425 sub CanItemBeReserved { Link Here
420
        return { status =>'alreadypossession' };
421
        return { status =>'alreadypossession' };
421
    }
422
    }
422
423
424
    # check if a recall exists on this item from this borrower
425
    return { status => 'recall' }
426
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
427
423
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
428
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
424
429
425
    my $reserves_control_branch;
430
    my $reserves_control_branch;
(-)a/C4/Search.pm (+9 lines)
Lines 1785-1790 sub searchResults { Link Here
1785
        my $item_in_transit_count = 0;
1785
        my $item_in_transit_count = 0;
1786
        my $item_onhold_count     = 0;
1786
        my $item_onhold_count     = 0;
1787
        my $notforloan_count      = 0;
1787
        my $notforloan_count      = 0;
1788
        my $item_recalled_count   = 0;
1788
        my $items_count           = scalar(@fields);
1789
        my $items_count           = scalar(@fields);
1789
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1790
        my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults');
1790
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
1791
        my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1;
Lines 1876-1881 sub searchResults { Link Here
1876
                # is item on the reserve shelf?
1877
                # is item on the reserve shelf?
1877
                my $reservestatus = '';
1878
                my $reservestatus = '';
1878
1879
1880
                # is item a waiting recall?
1881
                my $recallstatus = '';
1882
1879
                unless ($item->{withdrawn}
1883
                unless ($item->{withdrawn}
1880
                        || $item->{itemlost}
1884
                        || $item->{itemlost}
1881
                        || $item->{damaged}
1885
                        || $item->{damaged}
Lines 1897-1902 sub searchResults { Link Here
1897
                    #
1901
                    #
1898
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1902
                    ($transfertwhen, $transfertfrom, $transfertto) = C4::Circulation::GetTransfers($item->{itemnumber});
1899
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1903
                    $reservestatus = C4::Reserves::GetReserveStatus( $item->{itemnumber} );
1904
                    $recallstatus = 'Waiting' if Koha::Recalls->search({ itemnumber => $item->{itemnumber}, status => 'W' })->count;
1900
                }
1905
                }
1901
1906
1902
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
1907
                # item is withdrawn, lost, damaged, not for loan, reserved or in transit
Lines 1905-1910 sub searchResults { Link Here
1905
                    || $item->{damaged}
1910
                    || $item->{damaged}
1906
                    || $item->{notforloan}
1911
                    || $item->{notforloan}
1907
                    || $reservestatus eq 'Waiting'
1912
                    || $reservestatus eq 'Waiting'
1913
                    || $recallstatus eq 'Waiting'
1908
                    || ($transfertwhen && $transfertwhen ne ''))
1914
                    || ($transfertwhen && $transfertwhen ne ''))
1909
                {
1915
                {
1910
                    $withdrawn_count++        if $item->{withdrawn};
1916
                    $withdrawn_count++        if $item->{withdrawn};
Lines 1912-1917 sub searchResults { Link Here
1912
                    $itemdamaged_count++     if $item->{damaged};
1918
                    $itemdamaged_count++     if $item->{damaged};
1913
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1919
                    $item_in_transit_count++ if $transfertwhen && $transfertwhen ne '';
1914
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1920
                    $item_onhold_count++     if $reservestatus eq 'Waiting';
1921
                    $item_recalled_count++   if $recallstatus eq 'Waiting';
1915
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1922
                    $item->{status} = ($item->{withdrawn}//q{}) . "-" . ($item->{itemlost}//q{}) . "-" . ($item->{damaged}//q{}) . "-" . ($item->{notforloan}//q{});
1916
1923
1917
                    $other_count++;
1924
                    $other_count++;
Lines 1921-1926 sub searchResults { Link Here
1921
                        $other_items->{$key}->{$_} = $item->{$_};
1928
                        $other_items->{$key}->{$_} = $item->{$_};
1922
                    }
1929
                    }
1923
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1930
                    $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0;
1931
                    $other_items->{$key}->{recalled} = ($recallstatus) ? 1 : 0;
1924
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1932
                    $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0;
1925
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1933
                    $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan};
1926
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
1934
                    $other_items->{$key}->{count}++ if $item->{$hbranch};
Lines 2014-2019 sub searchResults { Link Here
2014
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2022
        $oldbiblio->{damagedcount}         = $itemdamaged_count;
2015
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2023
        $oldbiblio->{intransitcount}       = $item_in_transit_count;
2016
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2024
        $oldbiblio->{onholdcount}          = $item_onhold_count;
2025
        $oldbiblio->{recalledcount}        = $item_recalled_count;
2017
        $oldbiblio->{orderedcount}         = $ordered_count;
2026
        $oldbiblio->{orderedcount}         = $ordered_count;
2018
        $oldbiblio->{notforloancount}      = $notforloan_count;
2027
        $oldbiblio->{notforloancount}      = $notforloan_count;
2019
2028
(-)a/C4/XSLT.pm (-1 / +7 lines)
Lines 353-359 sub buildKohaItemsNamespace { Link Here
353
        my $status;
353
        my $status;
354
        my $substatus = '';
354
        my $substatus = '';
355
355
356
        if ($item->has_pending_hold) {
356
        my $recalls = Koha::Recalls->search({ itemnumber => $item->itemnumber, status => 'W' });
357
358
        if ( $recalls->count ) {
359
            # recalls take priority over holds
360
            $status = 'Waiting';
361
        }
362
        elsif ( $item->has_pending_hold ) {
357
            $status = 'other';
363
            $status = 'other';
358
            $substatus = 'Pending hold';
364
            $substatus = 'Pending hold';
359
        }
365
        }
(-)a/Koha/Biblio.pm (+108 lines)
Lines 1162-1167 sub get_marc_host { Link Here
1162
    }
1162
    }
1163
}
1163
}
1164
1164
1165
=head3 recalls
1166
1167
    my @recalls = $biblio->recalls;
1168
1169
Return all active recalls attached to this biblio, sorted by oldest first
1170
1171
=cut
1172
1173
sub recalls {
1174
    my ( $self ) = @_;
1175
    my @recalls_rs = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1176
    return @recalls_rs;
1177
}
1178
1179
=head3 can_be_recalled
1180
1181
    my @items_for_recall = $biblio->can_be_recalled({ patron => $patron_object });
1182
1183
Does biblio-level checks and returns the items attached to this biblio that are available for recall
1184
1185
=cut
1186
1187
sub can_be_recalled {
1188
    my ( $self, $params ) = @_;
1189
1190
    return 0 if !( C4::Context->preference('UseRecalls') );
1191
1192
    my $patron = $params->{patron};
1193
1194
    my $branchcode = C4::Context->userenv->{'branch'};
1195
    if ( C4::Context->preference('CircControl') eq 'PatronLibrary' and $patron ) {
1196
        $branchcode = $patron->branchcode;
1197
    }
1198
1199
    my @all_items = Koha::Items->search({ biblionumber => $self->biblionumber });
1200
1201
    # if there are no available items at all, no recall can be placed
1202
    return 0 if ( scalar @all_items == 0 );
1203
1204
    my @itemtypes;
1205
    my @itemnumbers;
1206
    my @items;
1207
    foreach my $item ( @all_items ) {
1208
        if ( $item->can_be_recalled({ patron => $patron }) ) {
1209
            push( @itemtypes, $item->effective_itemtype );
1210
            push( @itemnumbers, $item->itemnumber );
1211
            push( @items, $item );
1212
        }
1213
    }
1214
1215
    # if there are no recallable items, no recall can be placed
1216
    return 0 if ( scalar @items == 0 );
1217
1218
    # Check the circulation rule for each relevant itemtype for this biblio
1219
    my ( @recalls_allowed, @recalls_per_record, @on_shelf_recalls );
1220
    foreach my $itemtype ( @itemtypes ) {
1221
        my $rule = Koha::CirculationRules->get_effective_rules({
1222
            branchcode => $branchcode,
1223
            categorycode => $patron ? $patron->categorycode : undef,
1224
            itemtype => $itemtype,
1225
            rules => [
1226
                'recalls_allowed',
1227
                'recalls_per_record',
1228
                'on_shelf_recalls',
1229
            ],
1230
        });
1231
        push( @recalls_allowed, $rule->{recalls_allowed} ) if $rule;
1232
        push( @recalls_per_record, $rule->{recalls_per_record} ) if $rule;
1233
        push( @on_shelf_recalls, $rule->{on_shelf_recalls} ) if $rule;
1234
    }
1235
    my $recalls_allowed = (sort {$b <=> $a} @recalls_allowed)[0]; # take highest
1236
    my $recalls_per_record = (sort {$b <=> $a} @recalls_per_record)[0]; # take highest
1237
    my %on_shelf_recalls_count = ();
1238
    foreach my $count ( @on_shelf_recalls ) {
1239
        $on_shelf_recalls_count{$count}++;
1240
    }
1241
    my $on_shelf_recalls = (sort {$on_shelf_recalls_count{$b} <=> $on_shelf_recalls_count{$a}} @on_shelf_recalls)[0]; # take most common
1242
1243
    # check recalls allowed has been set and is not zero
1244
    return 0 if ( !defined($recalls_allowed) || $recalls_allowed == 0 );
1245
1246
    if ( $patron ) {
1247
        # check borrower has not reached open recalls allowed limit
1248
        return 0 if ( $patron->recalls->count >= $recalls_allowed );
1249
1250
        # check borrower has not reached open recalls allowed per record limit
1251
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $recalls_per_record );
1252
1253
        # check if any of the items under this biblio are already checked out by this borrower
1254
        return 0 if ( Koha::Checkouts->search({ itemnumber => [ @itemnumbers ], borrowernumber => $patron->borrowernumber })->count > 0 );
1255
    }
1256
1257
    # check item availability
1258
    my $checked_out_count = 0;
1259
    foreach (@items) {
1260
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1261
    }
1262
1263
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1264
    return 0 if ( $on_shelf_recalls eq 'all' && $checked_out_count < scalar @items );
1265
1266
    # can't recall if no items have been checked out
1267
    return 0 if ( $checked_out_count == 0 );
1268
1269
    # can recall
1270
    return @items;
1271
}
1272
1165
=head2 Internal methods
1273
=head2 Internal methods
1166
1274
1167
=head3 type
1275
=head3 type
(-)a/Koha/Item.pm (+176 lines)
Lines 1451-1456 sub _after_item_action_hooks { Link Here
1451
    );
1451
    );
1452
}
1452
}
1453
1453
1454
=head3 recall
1455
1456
    my $recall = $item->recall;
1457
1458
Return the relevant recall for this item
1459
1460
=cut
1461
1462
sub recall {
1463
    my ( $self ) = @_;
1464
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1465
    foreach my $recall (@recalls) {
1466
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1467
            return $recall;
1468
        }
1469
    }
1470
    # no item-level recall to return, so return earliest biblio-level
1471
    # FIXME: eventually this will be based on priority
1472
    return $recalls[0];
1473
}
1474
1475
=head3 can_be_recalled
1476
1477
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1478
1479
Does item-level checks and returns if items can be recalled by this borrower
1480
1481
=cut
1482
1483
sub can_be_recalled {
1484
    my ( $self, $params ) = @_;
1485
1486
    return 0 if !( C4::Context->preference('UseRecalls') );
1487
1488
    # check if this item is not for loan, withdrawn or lost
1489
    return 0 if ( $self->notforloan != 0 );
1490
    return 0 if ( $self->itemlost != 0 );
1491
    return 0 if ( $self->withdrawn != 0 );
1492
1493
    # check if this item is not checked out - if not checked out, can't be recalled
1494
    return 0 if ( !defined( $self->checkout ) );
1495
1496
    my $patron = $params->{patron};
1497
1498
    my $branchcode = C4::Context->userenv->{'branch'};
1499
    if ( $patron ) {
1500
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1501
    }
1502
1503
    # Check the circulation rule for each relevant itemtype for this item
1504
    my $rule = Koha::CirculationRules->get_effective_rules({
1505
        branchcode => $branchcode,
1506
        categorycode => $patron ? $patron->categorycode : undef,
1507
        itemtype => $self->effective_itemtype,
1508
        rules => [
1509
            'recalls_allowed',
1510
            'recalls_per_record',
1511
            'on_shelf_recalls',
1512
        ],
1513
    });
1514
1515
    # check recalls allowed has been set and is not zero
1516
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1517
1518
    if ( $patron ) {
1519
        # check borrower has not reached open recalls allowed limit
1520
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1521
1522
        # check borrower has not reach open recalls allowed per record limit
1523
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1524
1525
        # check if this patron has already recalled this item
1526
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1527
1528
        # check if this patron has already checked out this item
1529
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1530
1531
        # check if this patron has already reserved this item
1532
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1533
    }
1534
1535
    # check item availability
1536
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1537
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1538
1539
    # if there are no available items at all, no recall can be placed
1540
    return 0 if ( scalar @items == 0 );
1541
1542
    my $checked_out_count = 0;
1543
    foreach (@items) {
1544
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1545
    }
1546
1547
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1548
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1549
1550
    # can't recall if no items have been checked out
1551
    return 0 if ( $checked_out_count == 0 );
1552
1553
    # can recall
1554
    return 1;
1555
}
1556
1557
=head3 can_be_waiting_recall
1558
1559
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1560
1561
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1562
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1563
1564
=cut
1565
1566
sub can_be_waiting_recall {
1567
    my ( $self ) = @_;
1568
1569
    return 0 if !( C4::Context->preference('UseRecalls') );
1570
1571
    # check if this item is not for loan, withdrawn or lost
1572
    return 0 if ( $self->notforloan != 0 );
1573
    return 0 if ( $self->itemlost != 0 );
1574
    return 0 if ( $self->withdrawn != 0 );
1575
1576
    my $branchcode = $self->holdingbranch;
1577
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1578
        $branchcode = C4::Context->userenv->{'branch'};
1579
    } else {
1580
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1581
    }
1582
1583
    # Check the circulation rule for each relevant itemtype for this item
1584
    my $rule = Koha::CirculationRules->get_effective_rules({
1585
        branchcode => $branchcode,
1586
        categorycode => undef,
1587
        itemtype => $self->effective_itemtype,
1588
        rules => [
1589
            'recalls_allowed',
1590
        ],
1591
    });
1592
1593
    # check recalls allowed has been set and is not zero
1594
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1595
1596
    # can recall
1597
    return 1;
1598
}
1599
1600
=head3 check_recalls
1601
1602
    my $recall = $item->check_recalls;
1603
1604
Get the most relevant recall for this item.
1605
1606
=cut
1607
1608
sub check_recalls {
1609
    my ( $self ) = @_;
1610
1611
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1612
1613
    my $recall;
1614
    # iterate through relevant recalls to find the best one.
1615
    # if we come across a waiting recall, use this one.
1616
    # 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.
1617
    foreach my $r ( @recalls ) {
1618
        if ( $r->waiting ) {
1619
            $recall = $r;
1620
            last;
1621
        }
1622
    }
1623
    unless ( defined $recall ) {
1624
        $recall = $recalls[0];
1625
    }
1626
1627
    return $recall;
1628
}
1629
1454
=head3 _type
1630
=head3 _type
1455
1631
1456
=cut
1632
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 2054-2059 sub safe_to_delete { Link Here
2054
    return Koha::Result::Boolean->new(1);
2054
    return Koha::Result::Boolean->new(1);
2055
}
2055
}
2056
2056
2057
=head3 recalls
2058
2059
    my $recalls = $patron->recalls;
2060
2061
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
2062
2063
Return the patron's active recalls - total, or on a specific biblio
2064
2065
=cut
2066
2067
sub recalls {
2068
    my ( $self, $params ) = @_;
2069
2070
    my $biblionumber = $params->{biblionumber};
2071
2072
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
2073
2074
    if ( $biblionumber ) {
2075
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
2076
    }
2077
2078
    return $recalls_rs;
2079
}
2080
2057
=head2 Internal methods
2081
=head2 Internal methods
2058
2082
2059
=head3 _type
2083
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 26-31 use Koha::Holds; Link Here
26
use Koha::Biblios;
26
use Koha::Biblios;
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::Recalls;
29
30
30
sub HoldsCount {
31
sub HoldsCount {
31
    my ( $self, $biblionumber ) = @_;
32
    my ( $self, $biblionumber ) = @_;
Lines 56-59 sub CanArticleRequest { Link Here
56
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
}
58
}
58
59
60
sub RecallsCount {
61
    my ( $self, $biblionumber ) = @_;
62
63
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
64
65
    return $recalls->count;
66
}
67
59
1;
68
1;
(-)a/t/db_dependent/Circulation.t (-2 / +259 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
use utf8;
19
use utf8;
20
20
21
use Test::More tests => 57;
21
use Test::More tests => 60;
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 418-424 subtest "GetIssuingCharges tests" => sub { Link Here
418
418
419
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
419
my ( $reused_itemnumber_1, $reused_itemnumber_2 );
420
subtest "CanBookBeRenewed tests" => sub {
420
subtest "CanBookBeRenewed tests" => sub {
421
    plan tests => 93;
421
    plan tests => 97;
422
422
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
424
    # Generate test biblio
424
    # Generate test biblio
Lines 1429-1434 subtest "CanBookBeRenewed tests" => sub { Link Here
1429
            $item_3->itemcallnumber || '' ),
1429
            $item_3->itemcallnumber || '' ),
1430
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1430
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1431
    );
1431
    );
1432
1433
    # Recalls
1434
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1435
    Koha::CirculationRules->set_rules({
1436
        categorycode => undef,
1437
        branchcode => undef,
1438
        itemtype => undef,
1439
        rules => {
1440
            recalls_allowed => 10,
1441
            renewalsallowed => 5,
1442
        },
1443
    });
1444
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1445
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1446
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1447
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1448
1449
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1450
1451
    # item-level and this item: renewal not allowed
1452
    my $recall = Koha::Recall->new({
1453
        biblionumber => $recall_item1->biblionumber,
1454
        itemnumber => $recall_item1->itemnumber,
1455
        borrowernumber => $recall_borrower->borrowernumber,
1456
        branchcode => $recall_borrower->branchcode,
1457
        item_level_recall => 1,
1458
        status => 'R',
1459
    })->store;
1460
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1461
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1462
    $recall->set_cancelled;
1463
1464
    # biblio-level requested recall: renewal not allowed
1465
    $recall = Koha::Recall->new({
1466
        biblionumber => $recall_item1->biblionumber,
1467
        itemnumber => undef,
1468
        borrowernumber => $recall_borrower->borrowernumber,
1469
        branchcode => $recall_borrower->branchcode,
1470
        item_level_recall => 0,
1471
        status => 'R',
1472
    })->store;
1473
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1474
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1475
    $recall->set_cancelled;
1476
1477
    # item-level and not this item: renewal allowed
1478
    $recall = Koha::Recall->new({
1479
        biblionumber => $recall_item2->biblionumber,
1480
        itemnumber => $recall_item2->itemnumber,
1481
        borrowernumber => $recall_borrower->borrowernumber,
1482
        branchcode => $recall_borrower->branchcode,
1483
        item_level_recall => 1,
1484
        status => 'R',
1485
    })->store;
1486
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1487
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1488
    $recall->set_cancelled;
1489
1490
    # biblio-level waiting recall: renewal allowed
1491
    $recall = Koha::Recall->new({
1492
        biblionumber => $recall_item1->biblionumber,
1493
        itemnumber => undef,
1494
        borrowernumber => $recall_borrower->borrowernumber,
1495
        branchcode => $recall_borrower->branchcode,
1496
        item_level_recall => 0,
1497
        status => 'R',
1498
    })->store;
1499
    $recall->set_waiting({ item => $recall_item1 });
1500
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1501
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1502
    $recall->set_cancelled;
1432
};
1503
};
1433
1504
1434
subtest "GetUpcomingDueIssues" => sub {
1505
subtest "GetUpcomingDueIssues" => sub {
Lines 1909-1914 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1909
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1980
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1910
};
1981
};
1911
1982
1983
subtest 'AddIssue | recalls' => sub {
1984
    plan tests => 3;
1985
1986
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1987
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
1988
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
1989
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
1990
    my $item = $builder->build_sample_item;
1991
    Koha::CirculationRules->set_rules({
1992
        branchcode => undef,
1993
        itemtype => undef,
1994
        categorycode => undef,
1995
        rules => {
1996
            recalls_allowed => 10,
1997
        },
1998
    });
1999
2000
    # checking out item that they have recalled
2001
    my $recall1 = Koha::Recall->new({
2002
        borrowernumber => $patron1->borrowernumber,
2003
        biblionumber => $item->biblionumber,
2004
        itemnumber => $item->itemnumber,
2005
        item_level_recall => 1,
2006
        branchcode => $patron1->branchcode,
2007
        status => 'R',
2008
    })->store;
2009
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
2010
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
2011
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
2012
    AddReturn( $item->barcode, $item->homebranch );
2013
2014
    # this item is has a recall request. cancel recall
2015
    my $recall2 = Koha::Recall->new({
2016
        borrowernumber => $patron2->borrowernumber,
2017
        biblionumber => $item->biblionumber,
2018
        itemnumber => $item->itemnumber,
2019
        item_level_recall => 1,
2020
        branchcode => $patron2->branchcode,
2021
        status => 'R',
2022
    })->store;
2023
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
2024
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
2025
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
2026
    AddReturn( $item->barcode, $item->homebranch );
2027
2028
    # this item is waiting to fulfill a recall. revert recall
2029
    my $recall3 = Koha::Recall->new({
2030
        borrowernumber => $patron2->borrowernumber,
2031
        biblionumber => $item->biblionumber,
2032
        itemnumber => $item->itemnumber,
2033
        item_level_recall => 1,
2034
        branchcode => $patron2->branchcode,
2035
        status => 'R',
2036
    })->store;
2037
    $recall3->set_waiting;
2038
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
2039
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
2040
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
2041
    AddReturn( $item->barcode, $item->homebranch );
2042
};
2043
2044
1912
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
2045
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1913
    plan tests => 8;
2046
    plan tests => 8;
1914
2047
Lines 3827-3832 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3827
    # TODO test with AllowNotForLoanOverride = 1
3960
    # TODO test with AllowNotForLoanOverride = 1
3828
};
3961
};
3829
3962
3963
subtest 'CanBookBeIssued | recalls' => sub {
3964
    plan tests => 3;
3965
3966
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3967
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3968
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3969
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3970
    my $item = $builder->build_sample_item;
3971
    Koha::CirculationRules->set_rules({
3972
        branchcode => undef,
3973
        itemtype => undef,
3974
        categorycode => undef,
3975
        rules => {
3976
            recalls_allowed => 10,
3977
        },
3978
    });
3979
3980
    # item-level recall
3981
    my $recall = Koha::Recall->new({
3982
        borrowernumber => $patron1->borrowernumber,
3983
        biblionumber => $item->biblionumber,
3984
        itemnumber => $item->itemnumber,
3985
        item_level_recall => 1,
3986
        branchcode => $patron1->branchcode,
3987
        status => 'R',
3988
    })->store;
3989
3990
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
3991
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
3992
3993
    $recall->set_cancelled;
3994
3995
    # biblio-level recall
3996
    $recall = Koha::Recall->new({
3997
        borrowernumber => $patron1->borrowernumber,
3998
        biblionumber => $item->biblionumber,
3999
        itemnumber => undef,
4000
        item_level_recall => 0,
4001
        branchcode => $patron1->branchcode,
4002
        status => 'R',
4003
    })->store;
4004
4005
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4006
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
4007
4008
    $recall->set_cancelled;
4009
4010
    # biblio-level recall
4011
    $recall = Koha::Recall->new({
4012
        borrowernumber => $patron1->borrowernumber,
4013
        biblionumber => $item->biblionumber,
4014
        itemnumber => undef,
4015
        item_level_recall => 0,
4016
        branchcode => $patron1->branchcode,
4017
        status => 'R',
4018
    })->store;
4019
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
4020
4021
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
4022
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
4023
4024
    $recall->set_cancelled;
4025
};
4026
3830
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
4027
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3831
    plan tests => 1;
4028
    plan tests => 1;
3832
4029
Lines 3842-3847 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3842
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
4039
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3843
};
4040
};
3844
4041
4042
subtest 'AddReturn | recalls' => sub {
4043
    plan tests => 3;
4044
4045
    t::lib::Mocks::mock_preference("UseRecalls", 1);
4046
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
4047
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
4048
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
4049
    my $item1 = $builder->build_sample_item;
4050
    Koha::CirculationRules->set_rules({
4051
        branchcode => undef,
4052
        itemtype => undef,
4053
        categorycode => undef,
4054
        rules => {
4055
            recalls_allowed => 10,
4056
        },
4057
    });
4058
4059
    # this item can fill a recall with pickup at this branch
4060
    AddIssue( $patron1->unblessed, $item1->barcode );
4061
    my $recall1 = Koha::Recall->new({
4062
        borrowernumber => $patron2->borrowernumber,
4063
        biblionumber => $item1->biblionumber,
4064
        itemnumber => $item1->itemnumber,
4065
        item_level_recall => 1,
4066
        branchcode => $item1->homebranch,
4067
        status => 'R',
4068
    })->store;
4069
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4070
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
4071
    $recall1->set_cancelled;
4072
4073
    # this item can fill a recall but needs transfer
4074
    AddIssue( $patron1->unblessed, $item1->barcode );
4075
    $recall1 = Koha::Recall->new({
4076
        borrowernumber => $patron2->borrowernumber,
4077
        biblionumber => $item1->biblionumber,
4078
        itemnumber => $item1->itemnumber,
4079
        item_level_recall => 1,
4080
        branchcode => $patron2->branchcode,
4081
        status => 'R',
4082
    })->store;
4083
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4084
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
4085
    $recall1->set_cancelled;
4086
4087
    # this item is already in transit, do not ask to transfer
4088
    AddIssue( $patron1->unblessed, $item1->barcode );
4089
    $recall1 = Koha::Recall->new({
4090
        borrowernumber => $patron2->borrowernumber,
4091
        biblionumber => $item1->biblionumber,
4092
        itemnumber => $item1->itemnumber,
4093
        item_level_recall => 1,
4094
        branchcode => $patron2->branchcode,
4095
        status => 'R',
4096
    })->store;
4097
    $recall1->start_transfer;
4098
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
4099
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
4100
    $recall1->set_cancelled;
4101
};
3845
4102
3846
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
4103
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3847
4104
(-)a/t/db_dependent/Circulation/transferbook.t (-1 / +32 lines)
Lines 27-32 use Koha::DateUtils qw( dt_from_string ); Link Here
27
use Koha::Item::Transfers;
27
use Koha::Item::Transfers;
28
28
29
my $builder = t::lib::TestBuilder->new;
29
my $builder = t::lib::TestBuilder->new;
30
my $schema = Koha::Database->new->schema;
31
32
$schema->storage->txn_begin;
30
33
31
subtest 'transfer a non-existant item' => sub {
34
subtest 'transfer a non-existant item' => sub {
32
    plan tests => 2;
35
    plan tests => 2;
Lines 101-107 subtest 'field population tests' => sub { Link Here
101
#FIXME:'UseBranchTransferLimits tests missing
104
#FIXME:'UseBranchTransferLimits tests missing
102
105
103
subtest 'transfer already at destination' => sub {
106
subtest 'transfer already at destination' => sub {
104
    plan tests => 5;
107
    plan tests => 9;
105
108
106
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
109
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
107
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
110
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
Lines 151-156 subtest 'transfer already at destination' => sub { Link Here
151
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
154
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
152
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
155
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
153
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
156
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
157
158
    # recalls
159
    t::lib::Mocks::mock_preference('UseRecalls', 1);
160
    my $recall = Koha::Recall->new({
161
        biblionumber => $item->biblionumber,
162
        itemnumber => $item->itemnumber,
163
        item_level_recall => 1,
164
        borrowernumber => $patron->borrowernumber,
165
        branchcode => $library->branchcode,
166
        status => 'R',
167
    })->store;
168
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
169
    is( $dotransfer, 0, 'Do not transfer recalled item, it has already arrived' );
170
    is( $messages->{RecallPlacedAtHoldingBranch}, 1, "We found the recall");
171
172
    my $item2 = $builder->build_object({ class => 'Koha::Items' }); # this item will have a different holding branch to the pickup branch
173
    $recall = Koha::Recall->new({
174
        biblionumber => $item2->biblionumber,
175
        itemnumber => $item2->itemnumber,
176
        item_level_recall => 1,
177
        borrowernumber => $patron->borrowernumber,
178
        branchcode => $library->branchcode,
179
        status => 'R',
180
    })->store;
181
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
182
    is( $dotransfer, 1, 'Transfer of recalled item succeeded' );
183
    is( $messages->{RecallFound}->recall_id, $recall->recall_id, "We found the recall");
154
};
184
};
155
185
156
subtest 'transfer an issued item' => sub {
186
subtest 'transfer an issued item' => sub {
Lines 301-303 subtest 'transferbook test from branch' => sub { Link Here
301
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
331
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
302
332
303
};
333
};
334
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (+27 lines)
Lines 1549-1555 subtest 'non priority holds' => sub { Link Here
1549
    is( $err, 'on_reserve', 'Item is on hold' );
1549
    is( $err, 'on_reserve', 'Item is on hold' );
1550
1550
1551
    $schema->storage->txn_rollback;
1551
    $schema->storage->txn_rollback;
1552
};
1553
1554
subtest 'CanItemBeReserved / recall' => sub {
1555
    plan tests => 1;
1556
1557
    $schema->storage->txn_begin;
1558
1559
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1560
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1561
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1562
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1563
    my $item1   = $builder->build_sample_item(
1564
        {
1565
            biblionumber => $biblio1->biblionumber,
1566
            library      => $library1->branchcode
1567
        }
1568
    );
1569
    Koha::Recall->new({
1570
        borrowernumber => $patron1->borrowernumber,
1571
        biblionumber => $biblio1->biblionumber,
1572
        branchcode => $library1->branchcode,
1573
        itemnumber => $item1->itemnumber,
1574
        recalldate => '2020-05-04 10:10:10',
1575
        item_level_recall => 1,
1576
    })->store;
1577
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1552
1578
1579
    $schema->storage->txn_rollback;
1553
};
1580
};
1554
1581
1555
subtest 'CanItemBeReserved rule precedence tests' => sub {
1582
subtest 'CanItemBeReserved rule precedence tests' => sub {
(-)a/t/db_dependent/Koha/Biblio.t (-1 / +117 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 20;
20
use Test::More tests => 21; # +1
21
use Test::Warn;
21
use Test::Warn;
22
22
23
use C4::Biblio qw( AddBiblio ModBiblio ModBiblioMarc );
23
use C4::Biblio qw( AddBiblio ModBiblio ModBiblioMarc );
Lines 880-885 subtest 'get_marc_authors() tests' => sub { Link Here
880
    $biblio = Koha::Biblios->find( $biblio->biblionumber );
880
    $biblio = Koha::Biblios->find( $biblio->biblionumber );
881
881
882
    is( 4, @{$biblio->get_marc_authors}, 'get_marc_authors retrieves correct number of author subfields' );
882
    is( 4, @{$biblio->get_marc_authors}, 'get_marc_authors retrieves correct number of author subfields' );
883
    $schema->storage->txn_rollback;
884
};
885
886
subtest 'Recalls tests' => sub {
887
888
    plan tests => 12;
889
890
    $schema->storage->txn_begin;
891
    my $item1 = $builder->build_sample_item;
892
    my $biblio = $item1->biblio;
893
    my $branchcode = $item1->holdingbranch;
894
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
895
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
896
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
897
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
898
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
899
900
    my $recall1 = Koha::Recall->new({
901
        borrowernumber => $patron1->borrowernumber,
902
        recalldate => Koha::DateUtils::dt_from_string,
903
        biblionumber => $biblio->biblionumber,
904
        branchcode => $branchcode,
905
        status => 'R',
906
        itemnumber => $item1->itemnumber,
907
        expirationdate => undef,
908
        item_level_recall => 1
909
    })->store;
910
    my $recall2 = Koha::Recall->new({
911
        borrowernumber => $patron2->borrowernumber,
912
        recalldate => Koha::DateUtils::dt_from_string,
913
        biblionumber => $biblio->biblionumber,
914
        branchcode => $branchcode,
915
        status => 'R',
916
        itemnumber => undef,
917
        expirationdate => undef,
918
        item_level_recall => 0
919
    })->store;
920
    my $recall3 = Koha::Recall->new({
921
        borrowernumber => $patron3->borrowernumber,
922
        recalldate => Koha::DateUtils::dt_from_string,
923
        biblionumber => $biblio->biblionumber,
924
        branchcode => $branchcode,
925
        status => 'R',
926
        itemnumber => $item1->itemnumber,
927
        expirationdate => undef,
928
        item_level_recall => 1
929
    })->store;
930
931
    my $recalls_count = scalar $biblio->recalls;
932
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
933
934
    $recall1->set_cancelled;
935
    $recall2->set_expired({ interface => 'COMMANDLINE' });
936
937
    $recalls_count = scalar $biblio->recalls;
938
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
939
940
    t::lib::Mocks::mock_preference('UseRecalls', 0);
941
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
942
943
    t::lib::Mocks::mock_preference("UseRecalls", 1);
944
    $item1->update({ notforloan => 1 });
945
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
946
947
    $item1->update({ notforloan => 0 });
948
    Koha::CirculationRules->set_rules({
949
        branchcode => $branchcode,
950
        categorycode => $patron1->categorycode,
951
        itemtype => $item1->effective_itemtype,
952
        rules => {
953
            recalls_allowed => 0,
954
            recalls_per_record => 1,
955
            on_shelf_recalls => 'all',
956
        },
957
    });
958
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
959
960
    Koha::CirculationRules->set_rules({
961
        branchcode => $branchcode,
962
        categorycode => $patron1->categorycode,
963
        itemtype => $item1->effective_itemtype,
964
        rules => {
965
            recalls_allowed => 1,
966
            recalls_per_record => 1,
967
            on_shelf_recalls => 'all',
968
        },
969
    });
970
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
971
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
972
973
    $recall1->set_cancelled;
974
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
975
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
976
977
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
978
979
    Koha::CirculationRules->set_rules({
980
        branchcode => $branchcode,
981
        categorycode => $patron1->categorycode,
982
        itemtype => $item1->effective_itemtype,
983
        rules => {
984
            recalls_allowed => 1,
985
            recalls_per_record => 1,
986
            on_shelf_recalls => 'any',
987
        },
988
    });
989
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
990
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
991
992
    $recall2->set_cancelled;
993
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
994
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
995
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
996
997
    $item1->update({ withdrawn => 1 });
998
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
883
999
884
    $schema->storage->txn_rollback;
1000
    $schema->storage->txn_rollback;
885
};
1001
};
(-)a/t/db_dependent/Koha/Item.t (+186 lines)
Lines 1165-1171 subtest 'columns_to_str' => sub { Link Here
1165
    $cache->clear_from_cache("MarcSubfieldStructure-");
1165
    $cache->clear_from_cache("MarcSubfieldStructure-");
1166
1166
1167
    $schema->storage->txn_rollback;
1167
    $schema->storage->txn_rollback;
1168
};
1169
1170
subtest 'Recalls tests' => sub {
1171
1172
    plan tests => 20;
1173
1174
    $schema->storage->txn_begin;
1175
1176
    my $item1 = $builder->build_sample_item;
1177
    my $biblio = $item1->biblio;
1178
    my $branchcode = $item1->holdingbranch;
1179
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1180
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1181
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1182
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
1183
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
1184
1185
    my $recall1 = Koha::Recall->new({
1186
        borrowernumber => $patron1->borrowernumber,
1187
        recalldate => Koha::DateUtils::dt_from_string,
1188
        biblionumber => $biblio->biblionumber,
1189
        branchcode => $branchcode,
1190
        status => 'R',
1191
        itemnumber => $item1->itemnumber,
1192
        expirationdate => undef,
1193
        item_level_recall => 1
1194
    })->store;
1195
    my $recall2 = Koha::Recall->new({
1196
        borrowernumber => $patron2->borrowernumber,
1197
        recalldate => Koha::DateUtils::dt_from_string,
1198
        biblionumber => $biblio->biblionumber,
1199
        branchcode => $branchcode,
1200
        status => 'R',
1201
        itemnumber => $item1->itemnumber,
1202
        expirationdate => undef,
1203
        item_level_recall =>1
1204
    })->store;
1205
1206
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
1207
1208
    $recall2->set_cancelled;
1209
1210
    t::lib::Mocks::mock_preference('UseRecalls', 0);
1211
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
1212
1213
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1214
1215
    $item1->update({ notforloan => 1 });
1216
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
1217
    $item1->update({ notforloan => 0, itemlost => 1 });
1218
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
1219
    $item1->update({ itemlost => 0, withdrawn => 1 });
1220
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
1221
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
1222
1223
    $item1->update({ withdrawn => 0 });
1224
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1225
1226
    Koha::CirculationRules->set_rules({
1227
        branchcode => $branchcode,
1228
        categorycode => $patron1->categorycode,
1229
        itemtype => $item1->effective_itemtype,
1230
        rules => {
1231
            recalls_allowed => 0,
1232
            recalls_per_record => 1,
1233
            on_shelf_recalls => 'all',
1234
        },
1235
    });
1236
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
1237
1238
    Koha::CirculationRules->set_rules({
1239
        branchcode => $branchcode,
1240
        categorycode => $patron1->categorycode,
1241
        itemtype => $item1->effective_itemtype,
1242
        rules => {
1243
            recalls_allowed => 1,
1244
            recalls_per_record => 1,
1245
            on_shelf_recalls => 'all',
1246
        },
1247
    });
1248
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
1249
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
1250
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
1251
1252
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
1253
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
1254
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
1255
1256
    $recall1->set_cancelled;
1257
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
1258
1259
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
1260
1261
    Koha::CirculationRules->set_rules({
1262
        branchcode => $branchcode,
1263
        categorycode => $patron1->categorycode,
1264
        itemtype => $item1->effective_itemtype,
1265
        rules => {
1266
            recalls_allowed => 1,
1267
            recalls_per_record => 1,
1268
            on_shelf_recalls => 'any',
1269
        },
1270
    });
1271
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
1272
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
1273
1274
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1275
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
1276
1277
    $recall1 = Koha::Recall->new({
1278
        borrowernumber => $patron1->borrowernumber,
1279
        recalldate => Koha::DateUtils::dt_from_string,
1280
        biblionumber => $biblio->biblionumber,
1281
        branchcode => $branchcode,
1282
        status => 'R',
1283
        itemnumber => undef,
1284
        expirationdate => undef,
1285
        item_level_recall => 0
1286
    })->store;
1287
1288
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
1289
1290
    Koha::CirculationRules->set_rules({
1291
        branchcode => undef,
1292
        categorycode => undef,
1293
        itemtype => $item1->effective_itemtype,
1294
        rules => {
1295
            recalls_allowed => 0,
1296
            recalls_per_record => 1,
1297
            on_shelf_recalls => 'any',
1298
        },
1299
    });
1300
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
1301
1302
    Koha::CirculationRules->set_rules({
1303
        branchcode => undef,
1304
        categorycode => undef,
1305
        itemtype => $item1->effective_itemtype,
1306
        rules => {
1307
            recalls_allowed => 1,
1308
            recalls_per_record => 1,
1309
            on_shelf_recalls => 'any',
1310
        },
1311
    });
1312
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
1313
1314
    # check_recalls tests
1315
1316
    $recall1 = Koha::Recall->new({
1317
        borrowernumber => $patron2->borrowernumber,
1318
        recalldate => Koha::DateUtils::dt_from_string,
1319
        biblionumber => $biblio->biblionumber,
1320
        branchcode => $branchcode,
1321
        status => 'R',
1322
        itemnumber => $item1->itemnumber,
1323
        expirationdate => undef,
1324
        item_level_recall => 1
1325
    })->store;
1326
    $recall2 = Koha::Recall->new({
1327
        borrowernumber => $patron1->borrowernumber,
1328
        recalldate => Koha::DateUtils::dt_from_string,
1329
        biblionumber => $biblio->biblionumber,
1330
        branchcode => $branchcode,
1331
        status => 'R',
1332
        itemnumber => undef,
1333
        expirationdate => undef,
1334
        item_level_recall => 0
1335
    })->store;
1336
    $recall2->set_waiting({ item => $item1 });
1337
1338
    # return a waiting recall
1339
    my $check_recall = $item1->check_recalls;
1340
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
1341
1342
    $recall2->revert_waiting;
1343
1344
    # return recall based on recalldate
1345
    $check_recall = $item1->check_recalls;
1346
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
1347
1348
    $recall1->set_cancelled;
1349
1350
    # return a biblio-level recall
1351
    $check_recall = $item1->check_recalls;
1352
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
1168
1353
1354
    $recall2->set_cancelled;
1169
};
1355
};
1170
1356
1171
subtest 'store() tests' => sub {
1357
subtest 'store() tests' => sub {
(-)a/t/db_dependent/Koha/Patron.t (-1 / +52 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 14;
22
use Test::More tests => 15;
23
use Test::Exception;
23
use Test::Exception;
24
use Test::Warn;
24
use Test::Warn;
25
25
Lines 1048-1053 subtest 'messages' => sub { Link Here
1048
    is( $messages->count, 2, "There are two messages for this patron" );
1048
    is( $messages->count, 2, "There are two messages for this patron" );
1049
    is( $messages->next->message, $message_1->message );
1049
    is( $messages->next->message, $message_1->message );
1050
    is( $messages->next->message, $message_2->message );
1050
    is( $messages->next->message, $message_2->message );
1051
    $schema->storage->txn_rollback;
1052
};
1053
1054
subtest 'recalls() tests' => sub {
1055
1056
    plan tests => 2;
1057
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
1058
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
1059
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
1060
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
1061
1062
    Koha::Recall->new({
1063
        biblionumber => $biblio1->biblionumber,
1064
        borrowernumber => $patron->borrowernumber,
1065
        itemnumber => $item1->itemnumber,
1066
        branchcode => $patron->branchcode,
1067
        recalldate => dt_from_string,
1068
        status => 'R',
1069
        item_level_recall => 1,
1070
    })->store;
1071
    Koha::Recall->new({
1072
        biblionumber => $biblio2->biblionumber,
1073
        borrowernumber => $patron->borrowernumber,
1074
        itemnumber => $item2->itemnumber,
1075
        branchcode => $patron->branchcode,
1076
        recalldate => dt_from_string,
1077
        status => 'R',
1078
        item_level_recall => 1,
1079
    })->store;
1080
    Koha::Recall->new({
1081
        biblionumber => $biblio1->biblionumber,
1082
        borrowernumber => $patron->borrowernumber,
1083
        itemnumber => undef,
1084
        branchcode => $patron->branchcode,
1085
        recalldate => dt_from_string,
1086
        status => 'R',
1087
        item_level_recall => 0,
1088
    })->store;
1089
    my $recall = Koha::Recall->new({
1090
        biblionumber => $biblio1->biblionumber,
1091
        borrowernumber => $patron->borrowernumber,
1092
        itemnumber => undef,
1093
        branchcode => $patron->branchcode,
1094
        recalldate => dt_from_string,
1095
        status => 'R',
1096
        item_level_recall => 0,
1097
    })->store;
1098
    $recall->set_cancelled;
1099
1100
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
1101
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
1051
1102
1052
    $schema->storage->txn_rollback;
1103
    $schema->storage->txn_rollback;
1053
};
1104
};
(-)a/t/db_dependent/Stats.t (-1 / +1 lines)
Lines 55-61 $return_error = $@; Link Here
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
55
isnt ($return_error,'',"UpdateStats returns undef and croaks if type is undef");
56
56
57
# returns undef and croaks if mandatory params are missing
57
# returns undef and croaks if mandatory params are missing
58
my @allowed_circulation_types = qw (renew issue localuse return);
58
my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout recall);
59
my @allowed_accounts_types = qw (writeoff payment);
59
my @allowed_accounts_types = qw (writeoff payment);
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
60
my @circulation_mandatory_keys = qw (branch borrowernumber itemnumber ccode itemtype); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
61
my @accounts_mandatory_keys = qw (branch borrowernumber amount); #don't check type here
(-)a/t/db_dependent/XSLT.t (-3 / +14 lines)
Lines 48-54 subtest 'transformMARCXML4XSLT tests' => sub { Link Here
48
};
48
};
49
49
50
subtest 'buildKohaItemsNamespace status tests' => sub {
50
subtest 'buildKohaItemsNamespace status tests' => sub {
51
    plan tests => 16;
51
    plan tests => 17;
52
52
53
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
53
    t::lib::Mocks::mock_preference('Reference_NFL_Statuses', '1|2');
54
    t::lib::Mocks::mock_preference( 'OPACResultsLibrary', 'holdingbranch' );
54
    t::lib::Mocks::mock_preference( 'OPACResultsLibrary', 'holdingbranch' );
Lines 131-137 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
131
        }
131
        }
132
    });
132
    });
133
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
133
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
134
    like($xml,qr{<substatus>Waiting</substatus>},"Waiting status takes precedence over In transit");
134
    like($xml,qr{<substatus>Waiting</substatus>},"Waiting status takes precedence over In transit (holds)");
135
    $hold->cancel;
135
136
136
    $builder->build({ source => "TmpHoldsqueue", value => {
137
    $builder->build({ source => "TmpHoldsqueue", value => {
137
        itemnumber => $item->itemnumber
138
        itemnumber => $item->itemnumber
Lines 141-146 subtest 'buildKohaItemsNamespace status tests' => sub { Link Here
141
    like($xml,qr{<substatus>Pending hold</substatus>},"Pending status takes precedence over all");
142
    like($xml,qr{<substatus>Pending hold</substatus>},"Pending status takes precedence over all");
142
    my $library_name = $holdinglibrary->branchname;
143
    my $library_name = $holdinglibrary->branchname;
143
    like($xml,qr{<resultbranch>${library_name}</resultbranch>}, "Found resultbranch / holding branch" );
144
    like($xml,qr{<resultbranch>${library_name}</resultbranch>}, "Found resultbranch / holding branch" );
145
146
    my $recall = $builder->build_object({ class => 'Koha::Recalls', value => {
147
        biblionumber    => $item->biblionumber,
148
        itemnumber      => $item->itemnumber,
149
        branchcode      => $item->holdingbranch,
150
        status          => 'R',
151
    }});
152
    $recall->set_waiting;
153
    $xml = C4::XSLT::buildKohaItemsNamespace( $item->biblionumber,[]);
154
    like($xml,qr{<status>Waiting</status>},"Waiting status takes precedence over In transit (recalls)");
155
144
};
156
};
145
157
146
$schema->storage->txn_rollback;
158
$schema->storage->txn_rollback;
147
- 

Return to bug 19532