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 1945-1950 Value 1 if return is successful. Link Here
1945
2021
1946
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
2022
If AutomaticItemReturn is disabled, return branch is given as value of NeedsTransfer.
1947
2023
2024
=item C<RecallFound>
2025
2026
This item can fill a recall. The recall object is returned. If the recall pickup branch differs from
2027
the branch this item is being returned at, C<RecallNeedsTransfer> is also returned which contains this
2028
branchcode.
2029
2030
=item C<TransferredRecall>
2031
2032
This item has been transferred to this branch to fill a recall. The recall object is returned.
2033
1948
=back
2034
=back
1949
2035
1950
C<$iteminformation> is a reference-to-hash, giving information about the
2036
C<$iteminformation> is a reference-to-hash, giving information about the
Lines 2216-2221 sub AddReturn { Link Here
2216
        }
2302
        }
2217
    }
2303
    }
2218
2304
2305
    # find recalls...
2306
    # check if this item is recallable first, which includes checking if UseRecalls syspref is enabled
2307
    my $recall = undef;
2308
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2309
    if ( defined $recall ) {
2310
        $messages->{RecallFound} = $recall;
2311
        if ( $recall->branchcode ne $branch ) {
2312
            $messages->{RecallNeedsTransfer} = $branch;
2313
        }
2314
    }
2315
2219
    # find reserves.....
2316
    # find reserves.....
2220
    # launch the Checkreserves routine to find any holds
2317
    # launch the Checkreserves routine to find any holds
2221
    my ($resfound, $resrec);
2318
    my ($resfound, $resrec);
Lines 2275-2287 sub AddReturn { Link Here
2275
        $request->status('RET') if $request;
2372
        $request->status('RET') if $request;
2276
    }
2373
    }
2277
2374
2375
    my $transfer_recall = Koha::Recalls->find({ itemnumber => $item->itemnumber, status => 'T' }); # all recalls that have triggered a transfer will have an allocated itemnumber
2376
    if ( $transfer_recall and
2377
         $transfer_recall->branchcode eq $branch and
2378
         C4::Context->preference('UseRecalls') ) {
2379
        $messages->{TransferredRecall} = $transfer_recall;
2380
    }
2381
2278
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2382
    # Transfer to returnbranch if Automatic transfer set or append message NeedsTransfer
2279
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2383
    if ( $validTransfer && !C4::RotatingCollections::isItemInAnyCollection( $item->itemnumber )
2280
        && ( $doreturn or $messages->{'NotIssued'} )
2384
        && ( $doreturn or $messages->{'NotIssued'} )
2281
        and !$resfound
2385
        and !$resfound
2282
        and ( $branch ne $returnbranch )
2386
        and ( $branch ne $returnbranch )
2283
        and not $messages->{'WrongTransfer'}
2387
        and not $messages->{'WrongTransfer'}
2284
        and not $messages->{'WasTransfered'} )
2388
        and not $messages->{'WasTransfered'}
2389
        and not $messages->{TransferredRecall}
2390
        and not $messages->{RecallNeedsTransfer} )
2285
    {
2391
    {
2286
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2392
        my $BranchTransferLimitsType = C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ? 'effective_itemtype' : 'ccode';
2287
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
2393
        if  (C4::Context->preference("AutomaticItemReturn"    ) or
Lines 2802-2807 sub CanBookBeRenewed { Link Here
2802
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2908
        return ( 0, $auto_renew  ) if $auto_renew =~ 'auto_too_much_oweing';
2803
    }
2909
    }
2804
2910
2911
    my $recall = undef;
2912
    $recall = $item->check_recalls if $item->can_be_waiting_recall;
2913
    if ( defined $recall ) {
2914
        if ( $recall->item_level_recall ) {
2915
            # item-level recall. check if this item is the recalled item, otherwise renewal will be allowed
2916
            return ( 0, 'recalled' ) if ( $recall->itemnumber == $item->itemnumber );
2917
        } else {
2918
            # biblio-level recall, so only disallow renewal if the biblio-level recall has been fulfilled by a different item
2919
            return ( 0, 'recalled' ) unless ( $recall->waiting );
2920
        }
2921
    }
2922
2805
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2923
    my ( $resfound, $resrec, $possible_reserves ) = C4::Reserves::CheckReserves($itemnumber);
2806
2924
2807
    # If next hold is non priority, then check if any hold with priority (non_priority = 0) exists for the same biblionumber.
2925
    # 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 410-415 sub CanItemBeReserved { Link Here
410
        return { status =>'alreadypossession' };
411
        return { status =>'alreadypossession' };
411
    }
412
    }
412
413
414
    # check if a recall exists on this item from this borrower
415
    return { status => 'recall' }
416
      if Koha::Recalls->search({ borrowernumber => $borrowernumber, itemnumber => $itemnumber, old => undef })->count;
417
413
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
418
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
414
419
415
    my $querycount = q{
420
    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 1434-1439 sub _after_item_action_hooks { Link Here
1434
    );
1434
    );
1435
}
1435
}
1436
1436
1437
=head3 recall
1438
1439
    my $recall = $item->recall;
1440
1441
Return the relevant recall for this item
1442
1443
=cut
1444
1445
sub recall {
1446
    my ( $self ) = @_;
1447
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, old => undef }, { order_by => { -asc => 'recalldate' } });
1448
    foreach my $recall (@recalls) {
1449
        if ( $recall->item_level_recall and $recall->itemnumber == $self->itemnumber ){
1450
            return $recall;
1451
        }
1452
    }
1453
    # no item-level recall to return, so return earliest biblio-level
1454
    # FIXME: eventually this will be based on priority
1455
    return $recalls[0];
1456
}
1457
1458
=head3 can_be_recalled
1459
1460
    if ( $item->can_be_recalled({ patron => $patron_object }) ) # do recall
1461
1462
Does item-level checks and returns if items can be recalled by this borrower
1463
1464
=cut
1465
1466
sub can_be_recalled {
1467
    my ( $self, $params ) = @_;
1468
1469
    return 0 if !( C4::Context->preference('UseRecalls') );
1470
1471
    # check if this item is not for loan, withdrawn or lost
1472
    return 0 if ( $self->notforloan != 0 );
1473
    return 0 if ( $self->itemlost != 0 );
1474
    return 0 if ( $self->withdrawn != 0 );
1475
1476
    # check if this item is not checked out - if not checked out, can't be recalled
1477
    return 0 if ( !defined( $self->checkout ) );
1478
1479
    my $patron = $params->{patron};
1480
1481
    my $branchcode = C4::Context->userenv->{'branch'};
1482
    if ( $patron ) {
1483
        $branchcode = C4::Circulation::_GetCircControlBranch( $self->unblessed, $patron->unblessed );
1484
    }
1485
1486
    # Check the circulation rule for each relevant itemtype for this item
1487
    my $rule = Koha::CirculationRules->get_effective_rules({
1488
        branchcode => $branchcode,
1489
        categorycode => $patron ? $patron->categorycode : undef,
1490
        itemtype => $self->effective_itemtype,
1491
        rules => [
1492
            'recalls_allowed',
1493
            'recalls_per_record',
1494
            'on_shelf_recalls',
1495
        ],
1496
    });
1497
1498
    # check recalls allowed has been set and is not zero
1499
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1500
1501
    if ( $patron ) {
1502
        # check borrower has not reached open recalls allowed limit
1503
        return 0 if ( $patron->recalls->count >= $rule->{recalls_allowed} );
1504
1505
        # check borrower has not reach open recalls allowed per record limit
1506
        return 0 if ( $patron->recalls({ biblionumber => $self->biblionumber })->count >= $rule->{recalls_per_record} );
1507
1508
        # check if this patron has already recalled this item
1509
        return 0 if ( Koha::Recalls->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber, old => undef })->count > 0 );
1510
1511
        # check if this patron has already checked out this item
1512
        return 0 if ( Koha::Checkouts->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1513
1514
        # check if this patron has already reserved this item
1515
        return 0 if ( Koha::Holds->search({ itemnumber => $self->itemnumber, borrowernumber => $patron->borrowernumber })->count > 0 );
1516
    }
1517
1518
    # check item availability
1519
    # items are unavailable for recall if they are lost, withdrawn or notforloan
1520
    my @items = Koha::Items->search({ biblionumber => $self->biblionumber, itemlost => 0, withdrawn => 0, notforloan => 0 });
1521
1522
    # if there are no available items at all, no recall can be placed
1523
    return 0 if ( scalar @items == 0 );
1524
1525
    my $checked_out_count = 0;
1526
    foreach (@items) {
1527
        if ( Koha::Checkouts->search({ itemnumber => $_->itemnumber })->count > 0 ){ $checked_out_count++; }
1528
    }
1529
1530
    # can't recall if on shelf recalls only allowed when all unavailable, but items are still available for checkout
1531
    return 0 if ( $rule->{on_shelf_recalls} eq 'all' && $checked_out_count < scalar @items );
1532
1533
    # can't recall if no items have been checked out
1534
    return 0 if ( $checked_out_count == 0 );
1535
1536
    # can recall
1537
    return 1;
1538
}
1539
1540
=head3 can_be_waiting_recall
1541
1542
    if ( $item->can_be_waiting_recall ) { # allocate item as waiting for recall
1543
1544
Checks item type and branch of circ rules to return whether this item can be used to fill a recall.
1545
At this point the item has already been recalled. We are now at the checkin and set waiting stage.
1546
1547
=cut
1548
1549
sub can_be_waiting_recall {
1550
    my ( $self ) = @_;
1551
1552
    return 0 if !( C4::Context->preference('UseRecalls') );
1553
1554
    # check if this item is not for loan, withdrawn or lost
1555
    return 0 if ( $self->notforloan != 0 );
1556
    return 0 if ( $self->itemlost != 0 );
1557
    return 0 if ( $self->withdrawn != 0 );
1558
1559
    my $branchcode = $self->holdingbranch;
1560
    if ( C4::Context->preference('CircControl') eq 'PickupLibrary' and C4::Context->userenv and C4::Context->userenv->{'branch'} ) {
1561
        $branchcode = C4::Context->userenv->{'branch'};
1562
    } else {
1563
        $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch;
1564
    }
1565
1566
    # Check the circulation rule for each relevant itemtype for this item
1567
    my $rule = Koha::CirculationRules->get_effective_rules({
1568
        branchcode => $branchcode,
1569
        categorycode => undef,
1570
        itemtype => $self->effective_itemtype,
1571
        rules => [
1572
            'recalls_allowed',
1573
        ],
1574
    });
1575
1576
    # check recalls allowed has been set and is not zero
1577
    return 0 if ( !defined($rule->{recalls_allowed}) || $rule->{recalls_allowed} == 0 );
1578
1579
    # can recall
1580
    return 1;
1581
}
1582
1583
=head3 check_recalls
1584
1585
    my $recall = $item->check_recalls;
1586
1587
Get the most relevant recall for this item.
1588
1589
=cut
1590
1591
sub check_recalls {
1592
    my ( $self ) = @_;
1593
1594
    my @recalls = Koha::Recalls->search({ biblionumber => $self->biblionumber, itemnumber => [ $self->itemnumber, undef ], status => [ 'R','O','W','T' ] }, { order_by => { -asc => 'recalldate' } });
1595
1596
    my $recall;
1597
    # iterate through relevant recalls to find the best one.
1598
    # if we come across a waiting recall, use this one.
1599
    # 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.
1600
    foreach my $r ( @recalls ) {
1601
        if ( $r->waiting ) {
1602
            $recall = $r;
1603
            last;
1604
        }
1605
    }
1606
    unless ( defined $recall ) {
1607
        $recall = $recalls[0];
1608
    }
1609
1610
    return $recall;
1611
}
1612
1437
=head3 _type
1613
=head3 _type
1438
1614
1439
=cut
1615
=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 1540-1546 subtest 'non priority holds' => sub { Link Here
1540
    is( $err, 'on_reserve', 'Item is on hold' );
1540
    is( $err, 'on_reserve', 'Item is on hold' );
1541
1541
1542
    $schema->storage->txn_rollback;
1542
    $schema->storage->txn_rollback;
1543
};
1544
1545
subtest 'CanItemBeReserved / recall' => sub {
1546
    plan tests => 1;
1547
1548
    $schema->storage->txn_begin;
1549
1550
    my $itemtype1 = $builder->build_object( { class => 'Koha::ItemTypes' } );
1551
    my $library1  = $builder->build_object( { class => 'Koha::Libraries', value => {pickup_location => 1} } );
1552
    my $patron1   = $builder->build_object( { class => 'Koha::Patrons', value => {branchcode => $library1->branchcode} } );
1553
    my $biblio1 = $builder->build_sample_biblio({ itemtype => $itemtype1->itemtype });
1554
    my $item1   = $builder->build_sample_item(
1555
        {
1556
            biblionumber => $biblio1->biblionumber,
1557
            library      => $library1->branchcode
1558
        }
1559
    );
1560
    Koha::Recall->new({
1561
        borrowernumber => $patron1->borrowernumber,
1562
        biblionumber => $biblio1->biblionumber,
1563
        branchcode => $library1->branchcode,
1564
        itemnumber => $item1->itemnumber,
1565
        recalldate => '2020-05-04 10:10:10',
1566
        item_level_recall => 1,
1567
    })->store;
1568
    is( CanItemBeReserved( $patron1->borrowernumber, $item1->itemnumber, $library1->branchcode )->{status}, 'recall', "Can't reserve an item that they have already recalled" );
1543
1569
1570
    $schema->storage->txn_rollback;
1544
};
1571
};
1545
1572
1546
subtest 'CanItemBeReserved rule precedence tests' => sub {
1573
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 (-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 1165-1169 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
};
(-)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