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

(-)a/C4/Circulation.pm (-2 / +120 lines)
Lines 299-304 The item was reserved. The value is a reference-to-hash whose keys are fields fr Link Here
299
299
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
300
The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
301
301
302
=item C<RecallPlacedAtHoldingBranch>
303
304
A recall for this item was found, and the transfer has already been completed as the item's branch matches the recall's pickup branch.
305
306
=item C<RecallFound>
307
308
A recall for this item was found, and the item needs to be transferred to the recall's pickup branch.
309
302
=back
310
=back
303
311
304
=back
312
=back
Lines 372-377 sub transferbook { Link Here
372
        $dotransfer = 0 unless $ignoreRs;
380
        $dotransfer = 0 unless $ignoreRs;
373
    }
381
    }
374
382
383
    # find recall
384
    my $recall = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' });
385
    if ( defined $recall and C4::Context->preference('UseRecalls') ) {
386
        # do a transfer if the recall branch is different to the item holding branch
387
        if ( $recall->branchcode eq $fbr ) {
388
            $dotransfer = 0;
389
            $messages->{'RecallPlacedAtHoldingBranch'} = 1;
390
        } else {
391
            $dotransfer = 1;
392
            $messages->{'RecallFound'} = $recall;
393
        }
394
    }
395
375
    #actually do the transfer....
396
    #actually do the transfer....
376
    if ($dotransfer) {
397
    if ($dotransfer) {
377
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
398
        ModItemTransfer( $itemnumber, $fbr, $tbr, $trigger );
Lines 722-727 sticky due date is invalid or due date in the past Link Here
722
743
723
if the borrower borrows to much things
744
if the borrower borrows to much things
724
745
746
=head3 RECALLED
747
748
recalled by someone else
749
725
=cut
750
=cut
726
751
727
sub CanBookBeIssued {
752
sub CanBookBeIssued {
Lines 1095-1101 sub CanBookBeIssued { Link Here
1095
        }
1120
        }
1096
    }
1121
    }
1097
1122
1098
    unless ( $ignore_reserves ) {
1123
    my $recall;
1124
    # CHECK IF ITEM HAS BEEN RECALLED BY ANOTHER PATRON
1125
    # Only bother doing this if UseRecalls is enabled and the item is recallable
1126
    # Don't look at recalls that are in transit
1127
    if ( C4::Context->preference('UseRecalls') and $item_object->can_be_waiting_recall ) {
1128
        my @recalls = $biblio->recalls;
1129
1130
        foreach my $r ( @recalls ) {
1131
            if ( $r->itemnumber and
1132
                $r->itemnumber == $item_object->itemnumber and
1133
                $r->borrowernumber == $patron->borrowernumber and
1134
                $r->waiting ) {
1135
                $messages{RECALLED} = $r->recall_id;
1136
                $recall = $r;
1137
                # this item is already waiting for this borrower and the recall can be fulfilled
1138
                last;
1139
            }
1140
            elsif ( $r->itemnumber and
1141
                $r->itemnumber == $item_object->itemnumber and
1142
                $r->in_transit ) {
1143
                # recalled item is in transit
1144
                $issuingimpossible{RECALLED_INTRANSIT} = $r->branchcode;
1145
            }
1146
            elsif ( $r->item_level_recall and
1147
                $r->itemnumber == $item_object->itemnumber and
1148
                $r->borrowernumber != $patron->borrowernumber and
1149
                !$r->in_transit ) {
1150
                # this specific item has been recalled by a different patron
1151
                $needsconfirmation{RECALLED} = $r;
1152
                $recall = $r;
1153
                last;
1154
            }
1155
            elsif ( !$r->item_level_recall and
1156
                $r->borrowernumber != $patron->borrowernumber and
1157
                !$r->in_transit ) {
1158
                # a different patron has placed a biblio-level recall and this item is eligible to fill it
1159
                $needsconfirmation{RECALLED} = $r;
1160
                $recall = $r;
1161
                last;
1162
            }
1163
        }
1164
    }
1165
1166
    unless ( $ignore_reserves and defined $recall ) {
1099
        # See if the item is on reserve.
1167
        # See if the item is on reserve.
1100
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1168
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item_object->itemnumber );
1101
        if ($restype) {
1169
        if ($restype) {
Lines 1410-1415 AddIssue does the following things : Link Here
1410
          * RESERVE PLACED ?
1478
          * RESERVE PLACED ?
1411
              - fill reserve if reserve to this patron
1479
              - fill reserve if reserve to this patron
1412
              - cancel reserve or not, otherwise
1480
              - cancel reserve or not, otherwise
1481
          * RECALL PLACED ?
1482
              - fill recall if recall to this patron
1483
              - cancel recall or not
1484
              - revert recall's waiting status or not
1413
          * TRANSFERT PENDING ?
1485
          * TRANSFERT PENDING ?
1414
              - complete the transfert
1486
              - complete the transfert
1415
          * ISSUE THE BOOK
1487
          * ISSUE THE BOOK
Lines 1424-1429 sub AddIssue { Link Here
1424
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1496
    my $onsite_checkout = $params && $params->{onsite_checkout} ? 1 : 0;
1425
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1497
    my $switch_onsite_checkout = $params && $params->{switch_onsite_checkout};
1426
    my $auto_renew = $params && $params->{auto_renew};
1498
    my $auto_renew = $params && $params->{auto_renew};
1499
    my $cancel_recall = $params && $params->{cancel_recall};
1500
    my $recall_id = $params && $params->{recall_id};
1427
    my $dbh          = C4::Context->dbh;
1501
    my $dbh          = C4::Context->dbh;
1428
    my $barcodecheck = CheckValidBarcode($barcode);
1502
    my $barcodecheck = CheckValidBarcode($barcode);
1429
1503
Lines 1497-1502 sub AddIssue { Link Here
1497
                $item_object->discard_changes;
1571
                $item_object->discard_changes;
1498
            }
1572
            }
1499
1573
1574
            Koha::Recalls->move_recall({ action => $cancel_recall, recall_id => $recall_id, itemnumber => $item_object->itemnumber, borrowernumber => $borrower->{borrowernumber} }) if C4::Context->preference('UseRecalls');
1575
1500
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1576
            C4::Reserves::MoveReserve( $item_object->itemnumber, $borrower->{'borrowernumber'}, $cancelreserve );
1501
1577
1502
            # Starting process for transfer job (checking transfert and validate it if we have one)
1578
            # Starting process for transfer job (checking transfert and validate it if we have one)
Lines 1946-1951 Value 1 if return is successful. Link Here
1946
2022
1947
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2023
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1948
2024
2025
=item C<RecallFound>
2026
2027
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2028
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2029
branchcode.
2030
2031
=item C<TransferredRecall>
2032
2033
This item has been transferred to this branch to fill a recall. The recall object is returned.
2034
1949
=back
2035
=back
1950
2036
1951
C<$iteminformation> is a reference-to-hash, giving information about the
2037
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2217-2222 sub AddReturn { Link Here
2217
        }
2303
        }
2218
    }
2304
    }
2219
2305
2306
    # find recalls...
2307
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2308
    my $recall = undef;
2309
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2310
    if ( defined $recall ) {
2311
        $messages->{RecallFound} = $recall;
2312
        if ( $recall->branchcode ne $branch ) {
2313
            $messages->{RecallNeedsTransfer} = $branch;
2314
        }
2315
    }
2316
2220
    # find reserves.....
2317
    # find reserves.....
2221
    # launch the Checkreserves routine to find any holds
2318
    # launch the Checkreserves routine to find any holds
2222
    my ($resfound, $resrec);
2319
    my ($resfound, $resrec);
Lines 2276-2288 sub AddReturn { Link Here
2276
        $request->status('RET') if $request;
2373
        $request->status('RET') if $request;
2277
    }
2374
    }
2278
2375
2376
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2377
    if ( $transfer_recall and
2378
         $transfer_recall->branchcode eq $branch and
2379
         C4::Context->preference('UseRecalls') ) {
2380
        $messages->{TransferredRecall} = $transfer_recall;
2381
    }
2382
2279
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2383
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2280
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2384
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2281
        && ( $doreturn or $messages->{'NotIssued'} )
2385
        && ( $doreturn or $messages->{'NotIssued'} )
2282
        and !$resfound
2386
        and !$resfound
2283
        and ( $branch ne $returnbranch )
2387
        and ( $branch ne $returnbranch )
2284
        and not $messages->{'WrongTransfer'}
2388
        and not $messages->{'WrongTransfer'}
2285
        and not $messages->{'WasTransfered'} )
2389
        and not $messages->{'WasTransfered'}
2390
        and not $messages->{TransferredRecall}
2391
        and not $messages->{RecallNeedsTransfer} )
2286
    {
2392
    {
2287
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2393
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2288
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2394
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
Lines 2800-2805 sub CanBookBeRenewed { Link Here
2800
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2906
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2801
    }
2907
    }
2802
2908
2909
    my $recall = undef;
2910
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2911
    if ( defined $recall ) {
2912
        if ( $recall->item_level_recall ) {
2913
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2914
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2915
        } else {
2916
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2917
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2918
        }
2919
    }
2920
2803
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2921
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2804
2922
2805
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2923
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
(-)a/C4/Reserves.pm (+5 lines)
Lines 368-373 sub CanBookBeReserved{ Link Here
368
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
368
         { status => libraryNotPickupLocation },   if given branchcode is not configured to be a pickup location
369
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
369
         { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode
370
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
370
         { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups.
371
         { status => recall }, if the borrower has already placed a recall on this item
371
372
372
=cut
373
=cut
373
374
Lines 405-410 sub CanItemBeReserved { Link Here
405
        return { status =>'alreadypossession' };
406
        return { status =>'alreadypossession' };
406
    }
407
    }
407
408
409
    # check if a recall exists on this item from this borrower
410
    return { status => 'recall' }
411
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
412
408
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
413
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
409
414
410
    my $querycount = q{
415
    my $querycount = q{
(-)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 1427-1432 sub _after_item_action_hooks { Link Here
1427
    );
1427
    );
1428
}
1428
}
1429
1429
1430
=head3 recall
1431
1432
    my $recall = $item->recall;
1433
1434
Return the relevant recall for this item
1435
1436
=cut
1437
1438
sub recall {
1439
    my ( $self ) = @_;
1440
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1441
    foreach my $recall (@recalls) {
1442
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1443
            return $recall;
1444
        }
1445
    }
1446
    # no item-level recall to return, so return earliest biblio-level
1447
    # FIXME: eventually this will be based on priority
1448
    return $recalls[0];
1449
}
1450
1451
=head3 can_be_recalled
1452
1453
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1454
1455
Does item-level checks and returns if items can be recalled by this borrower
1456
1457
=cut
1458
1459
sub can_be_recalled {
1460
    my ( $self, $params ) = @_;
1461
1462
    return 0 if !( C4::Context->preference('UseRecalls') );
1463
1464
    # check if this item is not for loan, withdrawn or lost
1465
    return 0 if ( $self->notforloan != 0 );
1466
    return 0 if ( $self->itemlost != 0 );
1467
    return 0 if ( $self->withdrawn != 0 );
1468
1469
    # check if this item is not checked out - if not checked out, can't be recalled
1470
    return 0 if ( !defined( $self->checkout ) );
1471
1472
    my $patron = $params->{patron};
1473
1474
    my $branchcode = C4::Context->userenv->{'branch'};
1475
    if ( $patron ) {
1476
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1477
    }
1478
1479
    # Check the circulation rule for each relevant itemtype for this item
1480
    my $rule = Koha::CirculationRules->get_effective_rules({
1481
        branchcode => $branchcode,
1482
        categorycode => $patron ? $patron->categorycode : undef,
1483
        itemtype => $self->effective_itemtype,
1484
        rules => [
1485
            'recalls_allowed',
1486
            'recalls_per_record',
1487
            'on_shelf_recalls',
1488
        ],
1489
    });
1490
1491
    # check recalls allowed has been set and is not zero
1492
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1493
1494
    if ( $patron ) {
1495
        # check borrower has not reached open recalls allowed limit
1496
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1497
1498
        # check borrower has not reach open recalls allowed per record limit
1499
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1500
1501
        # check if this patron has already recalled this item
1502
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1503
1504
        # check if this patron has already checked out this item
1505
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1506
1507
        # check if this patron has already reserved this item
1508
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1509
    }
1510
1511
    # check item availability
1512
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1513
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1514
1515
    # if there are no available items at all, no recall can be placed
1516
    return 0 if ( scalar @items == 0 );
1517
1518
    my $checked_out_count = 0;
1519
    foreach (@items) {
1520
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1521
    }
1522
1523
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1524
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1525
1526
    # can't recall if no items have been checked out
1527
    return 0 if ( $checked_out_count == 0 );
1528
1529
    # can recall
1530
    return 1;
1531
}
1532
1533
=head3 can_be_waiting_recall
1534
1535
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1536
1537
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1538
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1539
1540
=cut
1541
1542
sub can_be_waiting_recall {
1543
    my ( $self ) = @_;
1544
1545
    return 0 if !( C4::Context->preference('UseRecalls') );
1546
1547
    # check if this item is not for loan, withdrawn or lost
1548
    return 0 if ( $self->notforloan != 0 );
1549
    return 0 if ( $self->itemlost != 0 );
1550
    return 0 if ( $self->withdrawn != 0 );
1551
1552
    my $branchcode = $self->holdingbranch;
1553
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1554
        $branchcode = C4::Context->userenv->{'branch'};
1555
    } else {
1556
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1557
    }
1558
1559
    # Check the circulation rule for each relevant itemtype for this item
1560
    my $rule = Koha::CirculationRules->get_effective_rules({
1561
        branchcode => $branchcode,
1562
        categorycode => undef,
1563
        itemtype => $self->effective_itemtype,
1564
        rules => [
1565
            'recalls_allowed',
1566
        ],
1567
    });
1568
1569
    # check recalls allowed has been set and is not zero
1570
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1571
1572
    # can recall
1573
    return 1;
1574
}
1575
1576
=head3 check_recalls
1577
1578
    my $recall = $item->check_recalls;
1579
1580
Get the most relevant recall for this item.
1581
1582
=cut
1583
1584
sub check_recalls {
1585
    my ( $self ) = @_;
1586
1587
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1588
1589
    my $recall;
1590
    # iterate through relevant recalls to find the best one.
1591
    # if we come across a waiting recall, use this one.
1592
    # 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.
1593
    foreach my $r ( @recalls ) {
1594
        if ( $r->waiting ) {
1595
            $recall = $r;
1596
            last;
1597
        }
1598
    }
1599
    unless ( defined $recall ) {
1600
        $recall = $recalls[0];
1601
    }
1602
1603
    return $recall;
1604
}
1605
1430
=head3 _type
1606
=head3 _type
1431
1607
1432
=cut
1608
=cut
(-)a/Koha/Patron.pm (+24 lines)
Lines 1963-1968 sub safe_to_delete { Link Here
1963
    return Koha::Result::Boolean->new(1);
1963
    return Koha::Result::Boolean->new(1);
1964
}
1964
}
1965
1965
1966
=head3 recalls
1967
1968
    my $recalls = $patron->recalls;
1969
1970
    my $recalls = $patron->recalls({ biblionumber => $biblionumber });
1971
1972
Return the patron's active recalls - total, or on a specific biblio
1973
1974
=cut
1975
1976
sub recalls {
1977
    my ( $self, $params ) = @_;
1978
1979
    my $biblionumber = $params->{biblionumber};
1980
1981
    my $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef });
1982
1983
    if ( $biblionumber ) {
1984
        $recalls_rs = Koha::Recalls->search({ borrowernumber => $self->borrowernumber, old => undef, biblionumber => $biblionumber });
1985
    }
1986
1987
    return $recalls_rs;
1988
}
1989
1966
=head2 Internal methods
1990
=head2 Internal methods
1967
1991
1968
=head3 _type
1992
=head3 _type
(-)a/Koha/Template/Plugin/Biblio.pm (+9 lines)
Lines 26-31 use Koha::Holds; Link Here
26
use Koha::Biblios;
26
use Koha::Biblios;
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::ArticleRequests;
28
use Koha::ArticleRequests;
29
use Koha::Recalls;
29
30
30
sub HoldsCount {
31
sub HoldsCount {
31
    my ( $self, $biblionumber ) = @_;
32
    my ( $self, $biblionumber ) = @_;
Lines 56-59 sub CanArticleRequest { Link Here
56
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
    return $biblio ? $biblio->can_article_request( $borrower ) : 0;
57
}
58
}
58
59
60
sub RecallsCount {
61
    my ( $self, $biblionumber ) = @_;
62
63
    my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef });
64
65
    return $recalls->count;
66
}
67
59
1;
68
1;
(-)a/t/db_dependent/Circulation.t (-2 / +259 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
use utf8;
19
use utf8;
20
20
21
use Test::More tests => 56;
21
use Test::More tests => 59;
22
use Test::Exception;
22
use Test::Exception;
23
use Test::MockModule;
23
use Test::MockModule;
24
use Test::Deep qw( cmp_deeply );
24
use Test::Deep qw( cmp_deeply );
Lines 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 => 95;
421
    plan tests => 99;
422
422
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
423
    C4::Context->set_preference('ItemsDeniedRenewal','');
424
    # Generate test biblio
424
    # Generate test biblio
Lines 1447-1452 subtest "CanBookBeRenewed tests" => sub { Link Here
1447
            $item_3->itemcallnumber || '' ),
1447
            $item_3->itemcallnumber || '' ),
1448
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1448
        "Account line description must not contain 'Lost Items ', but be title, barcode, itemcallnumber"
1449
    );
1449
    );
1450
1451
    # Recalls
1452
    t::lib::Mocks::mock_preference('UseRecalls', 1);
1453
    Koha::CirculationRules->set_rules({
1454
        categorycode => undef,
1455
        branchcode => undef,
1456
        itemtype => undef,
1457
        rules => {
1458
            recalls_allowed => 10,
1459
            renewalsallowed => 5,
1460
        },
1461
    });
1462
    my $recall_borrower = $builder->build_object({ class => 'Koha::Patrons' });
1463
    my $recall_biblio = $builder->build_object({ class => 'Koha::Biblios' });
1464
    my $recall_item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1465
    my $recall_item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $recall_biblio->biblionumber } });
1466
1467
    AddIssue( $renewing_borrower, $recall_item1->barcode );
1468
1469
    # item-level and this item: renewal not allowed
1470
    my $recall = Koha::Recall->new({
1471
        biblionumber => $recall_item1->biblionumber,
1472
        itemnumber => $recall_item1->itemnumber,
1473
        borrowernumber => $recall_borrower->borrowernumber,
1474
        branchcode => $recall_borrower->branchcode,
1475
        item_level_recall => 1,
1476
        status => 'R',
1477
    })->store;
1478
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1479
    is( $error, 'recalled', 'Cannot renew item that has been recalled' );
1480
    $recall->set_cancelled;
1481
1482
    # biblio-level requested recall: renewal not allowed
1483
    $recall = Koha::Recall->new({
1484
        biblionumber => $recall_item1->biblionumber,
1485
        itemnumber => undef,
1486
        borrowernumber => $recall_borrower->borrowernumber,
1487
        branchcode => $recall_borrower->branchcode,
1488
        item_level_recall => 0,
1489
        status => 'R',
1490
    })->store;
1491
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1492
    is( $error, 'recalled', 'Cannot renew item if biblio is recalled and has no item allocated' );
1493
    $recall->set_cancelled;
1494
1495
    # item-level and not this item: renewal allowed
1496
    $recall = Koha::Recall->new({
1497
        biblionumber => $recall_item2->biblionumber,
1498
        itemnumber => $recall_item2->itemnumber,
1499
        borrowernumber => $recall_borrower->borrowernumber,
1500
        branchcode => $recall_borrower->branchcode,
1501
        item_level_recall => 1,
1502
        status => 'R',
1503
    })->store;
1504
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1505
    is( $renewokay, 1, 'Can renew item if item-level recall on biblio is not on this item' );
1506
    $recall->set_cancelled;
1507
1508
    # biblio-level waiting recall: renewal allowed
1509
    $recall = Koha::Recall->new({
1510
        biblionumber => $recall_item1->biblionumber,
1511
        itemnumber => undef,
1512
        borrowernumber => $recall_borrower->borrowernumber,
1513
        branchcode => $recall_borrower->branchcode,
1514
        item_level_recall => 0,
1515
        status => 'R',
1516
    })->store;
1517
    $recall->set_waiting({ item => $recall_item1 });
1518
    ( $renewokay, $error ) = CanBookBeRenewed( $renewing_borrowernumber, $recall_item1->itemnumber );
1519
    is( $renewokay, 1, 'Can renew item if biblio-level recall has already been allocated an item' );
1520
    $recall->set_cancelled;
1450
};
1521
};
1451
1522
1452
subtest "GetUpcomingDueIssues" => sub {
1523
subtest "GetUpcomingDueIssues" => sub {
Lines 1927-1932 subtest 'AddIssue & AllowReturnToBranch' => sub { Link Here
1927
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1998
    # TODO t::lib::Mocks::mock_preference('AllowReturnToBranch', 'homeorholdingbranch');
1928
};
1999
};
1929
2000
2001
subtest 'AddIssue | recalls' => sub {
2002
    plan tests => 3;
2003
2004
    t::lib::Mocks::mock_preference("UseRecalls", 1);
2005
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
2006
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
2007
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
2008
    my $item = $builder->build_sample_item;
2009
    Koha::CirculationRules->set_rules({
2010
        branchcode => undef,
2011
        itemtype => undef,
2012
        categorycode => undef,
2013
        rules => {
2014
            recalls_allowed => 10,
2015
        },
2016
    });
2017
2018
    # checking out item that they have recalled
2019
    my $recall1 = Koha::Recall->new({
2020
        borrowernumber => $patron1->borrowernumber,
2021
        biblionumber => $item->biblionumber,
2022
        itemnumber => $item->itemnumber,
2023
        item_level_recall => 1,
2024
        branchcode => $patron1->branchcode,
2025
        status => 'R',
2026
    })->store;
2027
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall1->recall_id } );
2028
    $recall1 = Koha::Recalls->find( $recall1->recall_id );
2029
    is( $recall1->finished, 1, 'Recall was fulfilled when patron checked out item' );
2030
    AddReturn( $item->barcode, $item->homebranch );
2031
2032
    # this item is has a recall request. cancel recall
2033
    my $recall2 = Koha::Recall->new({
2034
        borrowernumber => $patron2->borrowernumber,
2035
        biblionumber => $item->biblionumber,
2036
        itemnumber => $item->itemnumber,
2037
        item_level_recall => 1,
2038
        branchcode => $patron2->branchcode,
2039
        status => 'R',
2040
    })->store;
2041
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall2->recall_id, cancel_recall => 'cancel' } );
2042
    $recall2 = Koha::Recalls->find( $recall2->recall_id );
2043
    is( $recall2->cancelled, 1, 'Recall was cancelled when patron checked out item' );
2044
    AddReturn( $item->barcode, $item->homebranch );
2045
2046
    # this item is waiting to fulfill a recall. revert recall
2047
    my $recall3 = Koha::Recall->new({
2048
        borrowernumber => $patron2->borrowernumber,
2049
        biblionumber => $item->biblionumber,
2050
        itemnumber => $item->itemnumber,
2051
        item_level_recall => 1,
2052
        branchcode => $patron2->branchcode,
2053
        status => 'R',
2054
    })->store;
2055
    $recall3->set_waiting;
2056
    AddIssue( $patron1->unblessed, $item->barcode, undef, undef, undef, undef, { recall_id => $recall3->recall_id, cancel_recall => 'revert' } );
2057
    $recall3 = Koha::Recalls->find( $recall3->recall_id );
2058
    is( $recall3->requested, 1, 'Recall was reverted from waiting when patron checked out item' );
2059
    AddReturn( $item->barcode, $item->homebranch );
2060
};
2061
2062
1930
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
2063
subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub {
1931
    plan tests => 8;
2064
    plan tests => 8;
1932
2065
Lines 3845-3850 subtest 'CanBookBeIssued | notforloan' => sub { Link Here
3845
    # TODO test with AllowNotForLoanOverride = 1
3978
    # TODO test with AllowNotForLoanOverride = 1
3846
};
3979
};
3847
3980
3981
subtest 'CanBookBeIssued | recalls' => sub {
3982
    plan tests => 3;
3983
3984
    t::lib::Mocks::mock_preference("UseRecalls", 1);
3985
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
3986
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
3987
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
3988
    my $item = $builder->build_sample_item;
3989
    Koha::CirculationRules->set_rules({
3990
        branchcode => undef,
3991
        itemtype => undef,
3992
        categorycode => undef,
3993
        rules => {
3994
            recalls_allowed => 10,
3995
        },
3996
    });
3997
3998
    # item-level recall
3999
    my $recall = Koha::Recall->new({
4000
        borrowernumber => $patron1->borrowernumber,
4001
        biblionumber => $item->biblionumber,
4002
        itemnumber => $item->itemnumber,
4003
        item_level_recall => 1,
4004
        branchcode => $patron1->branchcode,
4005
        status => 'R',
4006
    })->store;
4007
4008
    my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4009
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed an item-level recall on this item" );
4010
4011
    $recall->set_cancelled;
4012
4013
    # biblio-level recall
4014
    $recall = Koha::Recall->new({
4015
        borrowernumber => $patron1->borrowernumber,
4016
        biblionumber => $item->biblionumber,
4017
        itemnumber => undef,
4018
        item_level_recall => 0,
4019
        branchcode => $patron1->branchcode,
4020
        status => 'R',
4021
    })->store;
4022
4023
    ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron2, $item->barcode, undef, undef, undef, undef );
4024
    is( $needsconfirmation->{RECALLED}->recall_id, $recall->recall_id, "Another patron has placed a biblio-level recall and this item is eligible to fill it" );
4025
4026
    $recall->set_cancelled;
4027
4028
    # biblio-level recall
4029
    $recall = Koha::Recall->new({
4030
        borrowernumber => $patron1->borrowernumber,
4031
        biblionumber => $item->biblionumber,
4032
        itemnumber => undef,
4033
        item_level_recall => 0,
4034
        branchcode => $patron1->branchcode,
4035
        status => 'R',
4036
    })->store;
4037
    $recall->set_waiting({ item => $item, expirationdate => dt_from_string() });
4038
4039
    my ( undef, undef, undef, $messages ) = CanBookBeIssued( $patron1, $item->barcode, undef, undef, undef, undef );
4040
    is( $messages->{RECALLED}, $recall->recall_id, "This book can be issued by this patron and they have placed a recall" );
4041
4042
    $recall->set_cancelled;
4043
};
4044
3848
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
4045
subtest 'AddReturn should clear items.onloan for unissued items' => sub {
3849
    plan tests => 1;
4046
    plan tests => 1;
3850
4047
Lines 3860-3865 subtest 'AddReturn should clear items.onloan for unissued items' => sub { Link Here
3860
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
4057
    is( $item->onloan, undef, 'AddReturn did clear items.onloan' );
3861
};
4058
};
3862
4059
4060
subtest 'AddReturn | recalls' => sub {
4061
    plan tests => 3;
4062
4063
    t::lib::Mocks::mock_preference("UseRecalls", 1);
4064
    t::lib::Mocks::mock_preference("item-level_itypes", 1);
4065
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons' });
4066
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons' });
4067
    my $item1 = $builder->build_sample_item;
4068
    Koha::CirculationRules->set_rules({
4069
        branchcode => undef,
4070
        itemtype => undef,
4071
        categorycode => undef,
4072
        rules => {
4073
            recalls_allowed => 10,
4074
        },
4075
    });
4076
4077
    # this item can fill a recall with pickup at this branch
4078
    AddIssue( $patron1->unblessed, $item1->barcode );
4079
    my $recall1 = Koha::Recall->new({
4080
        borrowernumber => $patron2->borrowernumber,
4081
        biblionumber => $item1->biblionumber,
4082
        itemnumber => $item1->itemnumber,
4083
        item_level_recall => 1,
4084
        branchcode => $item1->homebranch,
4085
        status => 'R',
4086
    })->store;
4087
    my ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4088
    is( $messages->{RecallFound}->recall_id, $recall1->recall_id, "Recall found" );
4089
    $recall1->set_cancelled;
4090
4091
    # this item can fill a recall but needs transfer
4092
    AddIssue( $patron1->unblessed, $item1->barcode );
4093
    $recall1 = Koha::Recall->new({
4094
        borrowernumber => $patron2->borrowernumber,
4095
        biblionumber => $item1->biblionumber,
4096
        itemnumber => $item1->itemnumber,
4097
        item_level_recall => 1,
4098
        branchcode => $patron2->branchcode,
4099
        status => 'R',
4100
    })->store;
4101
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $item1->homebranch );
4102
    is( $messages->{RecallNeedsTransfer}, $item1->homebranch, "Recall requiring transfer found" );
4103
    $recall1->set_cancelled;
4104
4105
    # this item is already in transit, do not ask to transfer
4106
    AddIssue( $patron1->unblessed, $item1->barcode );
4107
    $recall1 = Koha::Recall->new({
4108
        borrowernumber => $patron2->borrowernumber,
4109
        biblionumber => $item1->biblionumber,
4110
        itemnumber => $item1->itemnumber,
4111
        item_level_recall => 1,
4112
        branchcode => $patron2->branchcode,
4113
        status => 'R',
4114
    })->store;
4115
    $recall1->start_transfer;
4116
    ( $doreturn, $messages, $iteminfo, $borrowerinfo ) = AddReturn( $item1->barcode, $patron2->branchcode );
4117
    is( $messages->{TransferredRecall}->recall_id, $recall1->recall_id, "In transit recall found" );
4118
    $recall1->set_cancelled;
4119
};
3863
4120
3864
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
4121
subtest 'AddRenewal and AddIssuingCharge tests' => sub {
3865
4122
(-)a/t/db_dependent/Circulation/transferbook.t (-1 / +32 lines)
Lines 27-32 use Koha::DateUtils qw( dt_from_string ); Link Here
27
use Koha::Item::Transfers;
27
use Koha::Item::Transfers;
28
28
29
my $builder = t::lib::TestBuilder->new;
29
my $builder = t::lib::TestBuilder->new;
30
my $schema = Koha::Database->new->schema;
31
32
$schema->storage->txn_begin;
30
33
31
subtest 'transfer a non-existant item' => sub {
34
subtest 'transfer a non-existant item' => sub {
32
    plan tests => 2;
35
    plan tests => 2;
Lines 101-107 subtest 'field population tests' => sub { Link Here
101
#FIXME:'UseBranchTransferLimits tests missing
104
#FIXME:'UseBranchTransferLimits tests missing
102
105
103
subtest 'transfer already at destination' => sub {
106
subtest 'transfer already at destination' => sub {
104
    plan tests => 5;
107
    plan tests => 9;
105
108
106
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
109
    my $library = $builder->build_object( { class => 'Koha::Libraries' } )->store;
107
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
110
    t::lib::Mocks::mock_userenv( { branchcode => $library->branchcode } );
Lines 151-156 subtest 'transfer already at destination' => sub { Link Here
151
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
154
    is( $dotransfer, 0, 'Transfer of reserved item doesn\'t succeed without ignore_reserves' );
152
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
155
    is( $messages->{ResFound}->{ResFound}, 'Reserved', "We found the reserve");
153
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
156
    is( $messages->{ResFound}->{itemnumber}, $item->itemnumber, "We got the reserve info");
157
158
    # recalls
159
    t::lib::Mocks::mock_preference('UseRecalls', 1);
160
    my $recall = Koha::Recall->new({
161
        biblionumber => $item->biblionumber,
162
        itemnumber => $item->itemnumber,
163
        item_level_recall => 1,
164
        borrowernumber => $patron->borrowernumber,
165
        branchcode => $library->branchcode,
166
        status => 'R',
167
    })->store;
168
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
169
    is( $dotransfer, 0, 'Do not transfer recalled item, it has already arrived' );
170
    is( $messages->{RecallPlacedAtHoldingBranch}, 1, "We found the recall");
171
172
    my $item2 = $builder->build_object({ class => 'Koha::Items' }); # this item will have a different holding branch to the pickup branch
173
    $recall = Koha::Recall->new({
174
        biblionumber => $item2->biblionumber,
175
        itemnumber => $item2->itemnumber,
176
        item_level_recall => 1,
177
        borrowernumber => $patron->borrowernumber,
178
        branchcode => $library->branchcode,
179
        status => 'R',
180
    })->store;
181
    ( $recall, $dotransfer, $messages ) = $recall->start_transfer;
182
    is( $dotransfer, 1, 'Transfer of recalled item succeeded' );
183
    is( $messages->{RecallFound}->recall_id, $recall->recall_id, "We found the recall");
154
};
184
};
155
185
156
subtest 'transfer an issued item' => sub {
186
subtest 'transfer an issued item' => sub {
Lines 301-303 subtest 'transferbook test from branch' => sub { Link Here
301
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
331
    is( $to_branch, $library->branchcode, 'The transfer is initiated to the specified branch');
302
332
303
};
333
};
334
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Holds.t (+27 lines)
Lines 1539-1545 subtest 'non priority holds' => sub { Link Here
1539
    is( $err, 'on_reserve', 'Item is on hold' );
1539
    is( $err, 'on_reserve', 'Item is on hold' );
1540
1540
1541
    $schema->storage->txn_rollback;
1541
    $schema->storage->txn_rollback;
1542
};
1543
1544
subtest 'CanItemBeReserved / recall' => sub {
1545
    plan tests => 1;
1546
1547
    $schema->storage->txn_begin;
1548
1549
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1550
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1551
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1552
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1553
    my $item1   = $builder->build_sample_item(
1554
        {
1555
            biblionumber => $biblio1->biblionumber,
1556
            library      => $library1->branchcode
1557
        }
1558
    );
1559
    Koha::Recall->new({
1560
        borrowernumber => $patron1->borrowernumber,
1561
        biblionumber => $biblio1->biblionumber,
1562
        branchcode => $library1->branchcode,
1563
        itemnumber => $item1->itemnumber,
1564
        recalldate => '2020-05-04 10:10:10',
1565
        item_level_recall => 1,
1566
    })->store;
1567
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1542
1568
1569
    $schema->storage->txn_rollback;
1543
};
1570
};
1544
1571
1545
subtest 'CanItemBeReserved rule precedence tests' => sub {
1572
subtest 'CanItemBeReserved rule precedence tests' => sub {
(-)a/t/db_dependent/Koha/Biblio.t (-1 / +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 883-888 subtest 'get_marc_authors() tests' => sub { Link Here
883
    $biblio = Koha::Biblios->find( $biblio->biblionumber );
883
    $biblio = Koha::Biblios->find( $biblio->biblionumber );
884
884
885
    is( 4, @{$biblio->get_marc_authors}, 'get_marc_authors retrieves correct number of author subfields' );
885
    is( 4, @{$biblio->get_marc_authors}, 'get_marc_authors retrieves correct number of author subfields' );
886
    $schema->storage->txn_rollback;
887
};
888
889
subtest 'Recalls tests' => sub {
890
891
    plan tests => 12;
892
893
    $schema->storage->txn_begin;
894
    my $item1 = $builder->build_sample_item;
895
    my $biblio = $item1->biblio;
896
    my $branchcode = $item1->holdingbranch;
897
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
898
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
899
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
900
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
901
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
902
903
    my $recall1 = Koha::Recall->new({
904
        borrowernumber => $patron1->borrowernumber,
905
        recalldate => Koha::DateUtils::dt_from_string,
906
        biblionumber => $biblio->biblionumber,
907
        branchcode => $branchcode,
908
        status => 'R',
909
        itemnumber => $item1->itemnumber,
910
        expirationdate => undef,
911
        item_level_recall => 1
912
    })->store;
913
    my $recall2 = Koha::Recall->new({
914
        borrowernumber => $patron2->borrowernumber,
915
        recalldate => Koha::DateUtils::dt_from_string,
916
        biblionumber => $biblio->biblionumber,
917
        branchcode => $branchcode,
918
        status => 'R',
919
        itemnumber => undef,
920
        expirationdate => undef,
921
        item_level_recall => 0
922
    })->store;
923
    my $recall3 = Koha::Recall->new({
924
        borrowernumber => $patron3->borrowernumber,
925
        recalldate => Koha::DateUtils::dt_from_string,
926
        biblionumber => $biblio->biblionumber,
927
        branchcode => $branchcode,
928
        status => 'R',
929
        itemnumber => $item1->itemnumber,
930
        expirationdate => undef,
931
        item_level_recall => 1
932
    })->store;
933
934
    my $recalls_count = scalar $biblio->recalls;
935
    is( $recalls_count, 3, 'Correctly get number of active recalls for biblio' );
936
937
    $recall1->set_cancelled;
938
    $recall2->set_expired({ interface => 'COMMANDLINE' });
939
940
    $recalls_count = scalar $biblio->recalls;
941
    is( $recalls_count, 1, 'Correctly get number of active recalls for biblio' );
942
943
    t::lib::Mocks::mock_preference('UseRecalls', 0);
944
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
945
946
    t::lib::Mocks::mock_preference("UseRecalls", 1);
947
    $item1->update({ notforloan => 1 });
948
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with no available items" );
949
950
    $item1->update({ notforloan => 0 });
951
    Koha::CirculationRules->set_rules({
952
        branchcode => $branchcode,
953
        categorycode => $patron1->categorycode,
954
        itemtype => $item1->effective_itemtype,
955
        rules => {
956
            recalls_allowed => 0,
957
            recalls_per_record => 1,
958
            on_shelf_recalls => 'all',
959
        },
960
    });
961
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
962
963
    Koha::CirculationRules->set_rules({
964
        branchcode => $branchcode,
965
        categorycode => $patron1->categorycode,
966
        itemtype => $item1->effective_itemtype,
967
        rules => {
968
            recalls_allowed => 1,
969
            recalls_per_record => 1,
970
            on_shelf_recalls => 'all',
971
        },
972
    });
973
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
974
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
975
976
    $recall1->set_cancelled;
977
    C4::Circulation::AddIssue( $patron1->unblessed, $item2->barcode );
978
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
979
980
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
981
982
    Koha::CirculationRules->set_rules({
983
        branchcode => $branchcode,
984
        categorycode => $patron1->categorycode,
985
        itemtype => $item1->effective_itemtype,
986
        rules => {
987
            recalls_allowed => 1,
988
            recalls_per_record => 1,
989
            on_shelf_recalls => 'any',
990
        },
991
    });
992
    C4::Circulation::AddReturn( $item2->barcode, $branchcode );
993
    is( $biblio->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
994
995
    $recall2->set_cancelled;
996
    C4::Circulation::AddIssue( $patron2->unblessed, $item2->barcode );
997
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
998
    is( $biblio->can_be_recalled({ patron => $patron1 }), 2, "Can recall two items" );
999
1000
    $item1->update({ withdrawn => 1 });
1001
    is( $biblio->can_be_recalled({ patron => $patron1 }), 1, "Can recall one item" );
886
1002
887
    $schema->storage->txn_rollback;
1003
    $schema->storage->txn_rollback;
888
};
1004
};
(-)a/t/db_dependent/Koha/Item.t (-1 / +187 lines)
Lines 20-26 Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
use utf8;
21
use utf8;
22
22
23
use Test::More tests => 13;
23
use Test::More tests => 14;
24
use Test::Exception;
24
use Test::Exception;
25
25
26
use C4::Biblio qw( GetMarcSubfieldStructure );
26
use C4::Biblio qw( GetMarcSubfieldStructure );
Lines 1158-1162 subtest 'columns_to_str' => sub { Link Here
1158
    $cache->clear_from_cache("MarcSubfieldStructure-");
1158
    $cache->clear_from_cache("MarcSubfieldStructure-");
1159
1159
1160
    $schema->storage->txn_rollback;
1160
    $schema->storage->txn_rollback;
1161
};
1162
1163
subtest 'Recalls tests' => sub {
1164
1165
    plan tests => 20;
1166
1167
    $schema->storage->txn_begin;
1168
1169
    my $item1 = $builder->build_sample_item;
1170
    my $biblio = $item1->biblio;
1171
    my $branchcode = $item1->holdingbranch;
1172
    my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1173
    my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1174
    my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $branchcode } });
1175
    my $item2 = $builder->build_object({ class => 'Koha::Items', value => { holdingbranch => $branchcode, homebranch => $branchcode, biblionumber => $biblio->biblionumber, itype => $item1->effective_itemtype } });
1176
    t::lib::Mocks::mock_userenv({ patron => $patron1 });
1177
1178
    my $recall1 = Koha::Recall->new({
1179
        borrowernumber => $patron1->borrowernumber,
1180
        recalldate => Koha::DateUtils::dt_from_string,
1181
        biblionumber => $biblio->biblionumber,
1182
        branchcode => $branchcode,
1183
        status => 'R',
1184
        itemnumber => $item1->itemnumber,
1185
        expirationdate => undef,
1186
        item_level_recall => 1
1187
    })->store;
1188
    my $recall2 = Koha::Recall->new({
1189
        borrowernumber => $patron2->borrowernumber,
1190
        recalldate => Koha::DateUtils::dt_from_string,
1191
        biblionumber => $biblio->biblionumber,
1192
        branchcode => $branchcode,
1193
        status => 'R',
1194
        itemnumber => $item1->itemnumber,
1195
        expirationdate => undef,
1196
        item_level_recall =>1
1197
    })->store;
1198
1199
    is( $item1->recall->borrowernumber, $patron1->borrowernumber, 'Correctly returns most relevant recall' );
1200
1201
    $recall2->set_cancelled;
1202
1203
    t::lib::Mocks::mock_preference('UseRecalls', 0);
1204
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall with UseRecalls disabled" );
1205
1206
    t::lib::Mocks::mock_preference("UseRecalls", 1);
1207
1208
    $item1->update({ notforloan => 1 });
1209
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is not for loan" );
1210
    $item1->update({ notforloan => 0, itemlost => 1 });
1211
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is marked lost" );
1212
    $item1->update({ itemlost => 0, withdrawn => 1 });
1213
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall that is withdrawn" );
1214
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if not checked out" );
1215
1216
    $item1->update({ withdrawn => 0 });
1217
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1218
1219
    Koha::CirculationRules->set_rules({
1220
        branchcode => $branchcode,
1221
        categorycode => $patron1->categorycode,
1222
        itemtype => $item1->effective_itemtype,
1223
        rules => {
1224
            recalls_allowed => 0,
1225
            recalls_per_record => 1,
1226
            on_shelf_recalls => 'all',
1227
        },
1228
    });
1229
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if recalls_allowed = 0" );
1230
1231
    Koha::CirculationRules->set_rules({
1232
        branchcode => $branchcode,
1233
        categorycode => $patron1->categorycode,
1234
        itemtype => $item1->effective_itemtype,
1235
        rules => {
1236
            recalls_allowed => 1,
1237
            recalls_per_record => 1,
1238
            on_shelf_recalls => 'all',
1239
        },
1240
    });
1241
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_allowed" );
1242
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has more existing recall(s) than recalls_per_record" );
1243
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if patron has already recalled this item" );
1244
1245
    my $reserve_id = C4::Reserves::AddReserve({ branchcode => $branchcode, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber, itemnumber => $item1->itemnumber });
1246
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall item if patron has already reserved it" );
1247
    C4::Reserves::ModReserve({ rank => 'del', reserve_id => $reserve_id, branchcode => $branchcode, itemnumber => $item1->itemnumber, borrowernumber => $patron1->borrowernumber, biblionumber => $item1->biblionumber });
1248
1249
    $recall1->set_cancelled;
1250
    is( $item1->can_be_recalled({ patron => $patron2 }), 0, "Can't recall if patron has already checked out an item attached to this biblio" );
1251
1252
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if on_shelf_recalls = all and items are still available" );
1253
1254
    Koha::CirculationRules->set_rules({
1255
        branchcode => $branchcode,
1256
        categorycode => $patron1->categorycode,
1257
        itemtype => $item1->effective_itemtype,
1258
        rules => {
1259
            recalls_allowed => 1,
1260
            recalls_per_record => 1,
1261
            on_shelf_recalls => 'any',
1262
        },
1263
    });
1264
    C4::Circulation::AddReturn( $item1->barcode, $branchcode );
1265
    is( $item1->can_be_recalled({ patron => $patron1 }), 0, "Can't recall if no items are checked out" );
1266
1267
    C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
1268
    is( $item1->can_be_recalled({ patron => $patron1 }), 1, "Can recall item" );
1269
1270
    $recall1 = Koha::Recall->new({
1271
        borrowernumber => $patron1->borrowernumber,
1272
        recalldate => Koha::DateUtils::dt_from_string,
1273
        biblionumber => $biblio->biblionumber,
1274
        branchcode => $branchcode,
1275
        status => 'R',
1276
        itemnumber => undef,
1277
        expirationdate => undef,
1278
        item_level_recall => 0
1279
    })->store;
1280
1281
    # Patron2 has Item1 checked out. Patron1 has placed a biblio-level recall on Biblio1, so check if Item1 can fulfill Patron1's recall.
1282
1283
    Koha::CirculationRules->set_rules({
1284
        branchcode => undef,
1285
        categorycode => undef,
1286
        itemtype => $item1->effective_itemtype,
1287
        rules => {
1288
            recalls_allowed => 0,
1289
            recalls_per_record => 1,
1290
            on_shelf_recalls => 'any',
1291
        },
1292
    });
1293
    is( $item1->can_be_waiting_recall, 0, "Recalls not allowed for this itemtype" );
1294
1295
    Koha::CirculationRules->set_rules({
1296
        branchcode => undef,
1297
        categorycode => undef,
1298
        itemtype => $item1->effective_itemtype,
1299
        rules => {
1300
            recalls_allowed => 1,
1301
            recalls_per_record => 1,
1302
            on_shelf_recalls => 'any',
1303
        },
1304
    });
1305
    is( $item1->can_be_waiting_recall, 1, "Recalls are allowed for this itemtype" );
1306
1307
    # check_recalls tests
1308
1309
    $recall1 = Koha::Recall->new({
1310
        borrowernumber => $patron2->borrowernumber,
1311
        recalldate => Koha::DateUtils::dt_from_string,
1312
        biblionumber => $biblio->biblionumber,
1313
        branchcode => $branchcode,
1314
        status => 'R',
1315
        itemnumber => $item1->itemnumber,
1316
        expirationdate => undef,
1317
        item_level_recall => 1
1318
    })->store;
1319
    $recall2 = Koha::Recall->new({
1320
        borrowernumber => $patron1->borrowernumber,
1321
        recalldate => Koha::DateUtils::dt_from_string,
1322
        biblionumber => $biblio->biblionumber,
1323
        branchcode => $branchcode,
1324
        status => 'R',
1325
        itemnumber => undef,
1326
        expirationdate => undef,
1327
        item_level_recall => 0
1328
    })->store;
1329
    $recall2->set_waiting({ item => $item1 });
1330
1331
    # return a waiting recall
1332
    my $check_recall = $item1->check_recalls;
1333
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Waiting recall is highest priority and returned" );
1334
1335
    $recall2->revert_waiting;
1336
1337
    # return recall based on recalldate
1338
    $check_recall = $item1->check_recalls;
1339
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "No waiting recall, so oldest recall is returned" );
1340
1341
    $recall1->set_cancelled;
1342
1343
    # return a biblio-level recall
1344
    $check_recall = $item1->check_recalls;
1345
    is( $check_recall->borrowernumber, $patron1->borrowernumber, "Only remaining recall is returned" );
1161
1346
1347
    $recall2->set_cancelled;
1162
};
1348
};
(-)a/t/db_dependent/Koha/Patron.t (+51 lines)
Lines 907-912 subtest 'safe_to_delete() tests' => sub { Link Here
907
    ok( $patron->safe_to_delete, 'Can delete, all conditions met' );
907
    ok( $patron->safe_to_delete, 'Can delete, all conditions met' );
908
    my $messages = $patron->safe_to_delete->messages;
908
    my $messages = $patron->safe_to_delete->messages;
909
    is_deeply( $messages, [], 'Patron can be deleted, no messages' );
909
    is_deeply( $messages, [], 'Patron can be deleted, no messages' );
910
    $schema->storage->txn_rollback;
911
};
912
913
subtest 'recalls() tests' => sub {
914
915
    plan tests => 2;
916
    my $biblio1 = $builder->build_object({ class => 'Koha::Biblios' });
917
    my $item1 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio1->biblionumber } });
918
    my $biblio2 = $builder->build_object({ class => 'Koha::Biblios' });
919
    my $item2 = $builder->build_object({ class => 'Koha::Items' }, { value => { biblionumber => $biblio2->biblionumber } });
920
921
    Koha::Recall->new({
922
        biblionumber => $biblio1->biblionumber,
923
        borrowernumber => $patron->borrowernumber,
924
        itemnumber => $item1->itemnumber,
925
        branchcode => $patron->branchcode,
926
        recalldate => dt_from_string,
927
        status => 'R',
928
        item_level_recall => 1,
929
    })->store;
930
    Koha::Recall->new({
931
        biblionumber => $biblio2->biblionumber,
932
        borrowernumber => $patron->borrowernumber,
933
        itemnumber => $item2->itemnumber,
934
        branchcode => $patron->branchcode,
935
        recalldate => dt_from_string,
936
        status => 'R',
937
        item_level_recall => 1,
938
    })->store;
939
    Koha::Recall->new({
940
        biblionumber => $biblio1->biblionumber,
941
        borrowernumber => $patron->borrowernumber,
942
        itemnumber => undef,
943
        branchcode => $patron->branchcode,
944
        recalldate => dt_from_string,
945
        status => 'R',
946
        item_level_recall => 0,
947
    })->store;
948
    my $recall = Koha::Recall->new({
949
        biblionumber => $biblio1->biblionumber,
950
        borrowernumber => $patron->borrowernumber,
951
        itemnumber => undef,
952
        branchcode => $patron->branchcode,
953
        recalldate => dt_from_string,
954
        status => 'R',
955
        item_level_recall => 0,
956
    })->store;
957
    $recall->set_cancelled;
958
959
    is( $patron->recalls->count, 3, "Correctly gets this patron's active recalls" );
960
    is( $patron->recalls({ biblionumber => $biblio1->biblionumber })->count, 2, "Correctly gets this patron's active recalls on a specific biblio" );
910
961
911
    $schema->storage->txn_rollback;
962
    $schema->storage->txn_rollback;
912
};
963
};
(-)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