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

(-)a/C4/Circulation.pm (-6 / +4 lines)
Lines 25-35 use Encode; Link Here
25
use Try::Tiny;
25
use Try::Tiny;
26
26
27
use C4::Context;
27
use C4::Context;
28
use C4::Stats qw( UpdateStats );
28
use C4::Stats    qw( UpdateStats );
29
use C4::Reserves
29
use C4::Reserves qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority );
30
    qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority IsAvailableForItemLevelRequest );
30
use C4::Biblio   qw( UpdateTotalIssues );
31
use C4::Biblio qw( UpdateTotalIssues );
31
use C4::Items    qw( ModItemTransfer ModDateLastSeen CartToShelf );
32
use C4::Items  qw( ModItemTransfer ModDateLastSeen CartToShelf );
33
use C4::Accounts;
32
use C4::Accounts;
34
use C4::ItemCirculationAlertPreference;
33
use C4::ItemCirculationAlertPreference;
35
use C4::Message;
34
use C4::Message;
Lines 3267-3273 sub CanBookBeRenewed { Link Here
3267
                foreach my $other_item (@other_items) {
3266
                foreach my $other_item (@other_items) {
3268
                    next if defined $matched_items{ $other_item->itemnumber };
3267
                    next if defined $matched_items{ $other_item->itemnumber };
3269
                    next if $other_item->holds->filter_by_found->count;
3268
                    next if $other_item->holds->filter_by_found->count;
3270
                    next unless IsAvailableForItemLevelRequest( $other_item, $patron_with_reserve, undef );
3271
                    next
3269
                    next
3272
                        unless CanItemBeReserved(
3270
                        unless CanItemBeReserved(
3273
                        $patron_with_reserve, $other_item, undef,
3271
                        $patron_with_reserve, $other_item, undef,
(-)a/C4/ILSDI/Services.pm (-6 / +4 lines)
Lines 24-30 use C4::Members; Link Here
24
use C4::Items       qw( get_hostitemnumbers_of );
24
use C4::Items       qw( get_hostitemnumbers_of );
25
use C4::Circulation qw( CanBookBeRenewed barcodedecode CanBookBeIssued AddRenewal );
25
use C4::Circulation qw( CanBookBeRenewed barcodedecode CanBookBeIssued AddRenewal );
26
use C4::Accounts;
26
use C4::Accounts;
27
use C4::Reserves qw( CanBookBeReserved IsAvailableForItemLevelRequest CalculatePriority AddReserve CanItemBeReserved );
27
use C4::Reserves qw( CanBookBeReserved CalculatePriority AddReserve CanItemBeReserved );
28
use C4::Context;
28
use C4::Context;
29
use C4::Auth;
29
use C4::Auth;
30
use CGI qw ( -utf8 );
30
use CGI qw ( -utf8 );
Lines 642-653 sub GetServices { Link Here
642
    my @availablefor;
642
    my @availablefor;
643
643
644
    # Reserve level management
644
    # Reserve level management
645
    my $biblionumber      = $item->biblionumber;
645
    my $biblionumber = $item->biblionumber;
646
    my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
646
    if ( CanBookBeReserved( $borrower, $biblionumber )->{status} eq 'OK' ) {
647
    if ( $canbookbereserved->{status} eq 'OK' ) {
648
        push @availablefor, 'title level hold';
647
        push @availablefor, 'title level hold';
649
        my $canitembereserved = IsAvailableForItemLevelRequest( $item, $patron );
648
        if ( CanItemBeReserved( $patron, $item )->{status} eq 'OK' ) {
650
        if ($canitembereserved) {
651
            push @availablefor, 'item level hold';
649
            push @availablefor, 'item level hold';
652
        }
650
        }
653
    }
651
    }
(-)a/C4/Reserves.pm (-51 lines)
Lines 123-129 BEGIN { Link Here
123
123
124
        AutoUnsuspendReserves
124
        AutoUnsuspendReserves
125
125
126
        IsAvailableForItemLevelRequest
127
        ItemsAnyAvailableAndNotRestricted
126
        ItemsAnyAvailableAndNotRestricted
128
127
129
        AlterPriority
128
        AlterPriority
Lines 1366-1421 sub ModReserveMinusPriority { Link Here
1366
    _FixPriority( { reserve_id => $reserve_id, rank => '0' } );
1365
    _FixPriority( { reserve_id => $reserve_id, rank => '0' } );
1367
}
1366
}
1368
1367
1369
=head2 IsAvailableForItemLevelRequest
1370
1371
  my $is_available = IsAvailableForItemLevelRequest( $item_record, $borrower_record, $pickup_branchcode );
1372
1373
Checks whether a given item record is available for an
1374
item-level hold request.  An item is available if
1375
1376
* it is not lost AND
1377
* it is not damaged AND
1378
* it is not withdrawn AND
1379
* a waiting or in transit reserve is placed on
1380
* does not have a not for loan value > 0
1381
1382
Need to check the issuingrules onshelfholds column,
1383
if this is set items on the shelf can be placed on hold
1384
1385
Note that IsAvailableForItemLevelRequest() does not
1386
check if the staff operator is authorized to place
1387
a request on the item - in particular,
1388
this routine does not check IndependentBranches
1389
and canreservefromotherbranches.
1390
1391
Note also that this subroutine does not checks smart
1392
rules limits for item by reservesallowed/holds_per_record
1393
values, this complemented in calling code with calls and
1394
checks with CanItemBeReserved or CanBookBeReserved.
1395
1396
=cut
1397
1398
sub IsAvailableForItemLevelRequest {
1399
    my $item              = shift;
1400
    my $patron            = shift;
1401
    my $pickup_branchcode = shift;
1402
1403
    if ($pickup_branchcode) {
1404
        my $destination = Koha::Libraries->find($pickup_branchcode);
1405
        return 0 unless $destination;
1406
        return 0 unless $destination->pickup_location;
1407
        return 0 unless $item->can_be_transferred( { to => $destination } );
1408
        my $reserves_control_branch = Koha::Policy::Holds->holds_control_library( $item, $patron );
1409
        my $branchitemrule          = C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->itype );
1410
        my $home_library            = Koha::Libraries->find( { branchcode => $item->homebranch } );
1411
        return 0
1412
            unless $branchitemrule->{hold_fulfillment_policy} ne 'holdgroup'
1413
            || $home_library->validate_hold_sibling( { branchcode => $pickup_branchcode } );
1414
    }
1415
1416
    return IsOnShelfHoldsPolicySatisfied( { item => $item, patron => $patron } );
1417
}
1418
1419
=head2 ItemsAnyAvailableAndNotRestricted
1368
=head2 ItemsAnyAvailableAndNotRestricted
1420
1369
1421
  ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron });
1370
  ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron });
(-)a/Koha/Item.pm (-1 / +4 lines)
Lines 411-417 Returns the itemtype for the item based on whether item level itemtypes are set Link Here
411
411
412
sub effective_itemtype {
412
sub effective_itemtype {
413
    my ($self) = @_;
413
    my ($self) = @_;
414
414
    if ( C4::Context->preference('item-level_itypes') ) {
415
        return undef
416
            unless defined $self->itype;
417
    }
415
    return $self->_result()->effective_itemtype();
418
    return $self->_result()->effective_itemtype();
416
}
419
}
417
420
(-)a/opac/opac-ISBDdetail.pl (-7 / +2 lines)
Lines 51-57 use C4::Biblio qw( Link Here
51
    TransformMarcToKoha
51
    TransformMarcToKoha
52
);
52
);
53
use C4::Record   qw( marc2cites );
53
use C4::Record   qw( marc2cites );
54
use C4::Reserves qw( IsAvailableForItemLevelRequest );
54
use C4::Reserves qw( CanBookBeReserved );
55
use C4::Serials  qw( CountSubscriptionFromBiblionumber SearchSubscriptions GetLatestSerials );
55
use C4::Serials  qw( CountSubscriptionFromBiblionumber SearchSubscriptions GetLatestSerials );
56
use C4::Koha     qw(
56
use C4::Koha     qw(
57
    GetNormalizedEAN
57
    GetNormalizedEAN
Lines 214-225 my $holdable_items = $biblio->items->filter_by_for_hold->count; Link Here
214
# If we don't have a patron, then holdable items determines holdability
214
# If we don't have a patron, then holdable items determines holdability
215
my $can_holds_be_placed = $patron ? 0 : $holdable_items;
215
my $can_holds_be_placed = $patron ? 0 : $holdable_items;
216
if ($patron) {
216
if ($patron) {
217
    my $items = $biblio->items;
217
    $can_holds_be_placed = CanBookBeReserved( $patron->id, $biblio->id );
218
    while ( my $item = $items->next ) {
219
        $can_holds_be_placed = $can_holds_be_placed
220
            || ( IsAvailableForItemLevelRequest( $item, $patron, undef )
221
            && CanItemBeReserved( $patron, $item, undef ) );
222
    }
223
}
218
}
224
219
225
$template->param(
220
$template->param(
(-)a/opac/opac-MARCdetail.pl (-7 / +2 lines)
Lines 57-63 use C4::Biblio qw( Link Here
57
    TransformMarcToKoha
57
    TransformMarcToKoha
58
);
58
);
59
use C4::Record   qw( marc2cites );
59
use C4::Record   qw( marc2cites );
60
use C4::Reserves qw( IsAvailableForItemLevelRequest );
60
use C4::Reserves qw( CanBookBeReserved );
61
use C4::Members;
61
use C4::Members;
62
use C4::Koha        qw( GetNormalizedISBN );
62
use C4::Koha        qw( GetNormalizedISBN );
63
use List::MoreUtils qw( uniq );
63
use List::MoreUtils qw( uniq );
Lines 175-186 my $holdable_items = $biblio->items->filter_by_for_hold->count; Link Here
175
my $can_holds_be_placed = $patron ? 0 : $holdable_items;
175
my $can_holds_be_placed = $patron ? 0 : $holdable_items;
176
176
177
if ($patron) {
177
if ($patron) {
178
    $items->reset;
178
    $can_holds_be_placed = CanBookBeReserved( $patron->id, $biblio->id );
179
    while ( my $item = $items->next ) {
180
        $can_holds_be_placed = $can_holds_be_placed
181
            || ( IsAvailableForItemLevelRequest( $item, $patron, undef )
182
            && CanItemBeReserved( $patron, $item, undef ) );
183
    }
184
}
179
}
185
180
186
$template->param( ReservableItems => $can_holds_be_placed );
181
$template->param( ReservableItems => $can_holds_be_placed );
(-)a/opac/opac-detail.pl (-4 / +2 lines)
Lines 59-65 use C4::External::Syndetics qw( Link Here
59
use C4::Members;
59
use C4::Members;
60
use C4::XSLT         qw( XSLTParse4Display );
60
use C4::XSLT         qw( XSLTParse4Display );
61
use C4::ShelfBrowser qw( GetNearbyItems );
61
use C4::ShelfBrowser qw( GetNearbyItems );
62
use C4::Reserves     qw( GetReserveStatus IsAvailableForItemLevelRequest );
62
use C4::Reserves     qw( GetReserveStatus CanItemBeReserved );
63
use C4::Charset      qw( SetUTF8Flag );
63
use C4::Charset      qw( SetUTF8Flag );
64
use MARC::Field;
64
use MARC::Field;
65
use List::MoreUtils qw( any );
65
use List::MoreUtils qw( any );
Lines 747-755 if ( not $viewallitems and $items->count > $max_items_to_display ) { Link Here
747
        # We only need to check if we haven't determined holds can be placed
747
        # We only need to check if we haven't determined holds can be placed
748
        # and if we don't have patron, we have already decided
748
        # and if we don't have patron, we have already decided
749
        $can_holds_be_placed = $can_holds_be_placed
749
        $can_holds_be_placed = $can_holds_be_placed
750
            || ( $patron
750
            || ( $patron && CanItemBeReserved( $patron, $item, undef ) );
751
            && IsAvailableForItemLevelRequest( $item, $patron, undef )
752
            && CanItemBeReserved( $patron, $item, undef ) );
753
751
754
        # get collection code description, too
752
        # get collection code description, too
755
        my $ccode = $item->ccode;
753
        my $ccode = $item->ccode;
(-)a/opac/opac-reserve.pl (-4 / +2 lines)
Lines 24-30 use CGI qw ( -utf8 ); Link Here
24
use C4::Auth        qw( get_template_and_user );
24
use C4::Auth        qw( get_template_and_user );
25
use C4::Koha        qw( getitemtypeimagelocation getitemtypeimagesrc );
25
use C4::Koha        qw( getitemtypeimagelocation getitemtypeimagesrc );
26
use C4::Circulation qw( GetBranchItemRule );
26
use C4::Circulation qw( GetBranchItemRule );
27
use C4::Reserves    qw( CanItemBeReserved CanBookBeReserved AddReserve IsAvailableForItemLevelRequest GetReserveFee );
27
use C4::Reserves    qw( CanItemBeReserved CanBookBeReserved AddReserve GetReserveFee );
28
use C4::Biblio      qw( GetBiblioData GetFrameworkCode );
28
use C4::Biblio      qw( GetBiblioData GetFrameworkCode );
29
use C4::Output      qw( output_html_with_http_headers );
29
use C4::Output      qw( output_html_with_http_headers );
30
use C4::Context;
30
use C4::Context;
Lines 418-424 foreach my $biblioNum (@biblionumbers) { Link Here
418
    my $numCopiesOPACAvailable = 0;
418
    my $numCopiesOPACAvailable = 0;
419
419
420
    # iterating through all items first to check if any of them available
420
    # iterating through all items first to check if any of them available
421
    # to pass this value further inside down to IsAvailableForItemLevelRequest to
422
    # it's complicated logic to analyse.
421
    # it's complicated logic to analyse.
423
    # (before this loop was inside that sub loop so it was O(n^2) )
422
    # (before this loop was inside that sub loop so it was O(n^2) )
424
    foreach my $item ( @{ $biblioData->{items} } ) {
423
    foreach my $item ( @{ $biblioData->{items} } ) {
Lines 461-468 foreach my $biblioNum (@biblionumbers) { Link Here
461
460
462
        my $branch = Koha::Policy::Holds->holds_control_library( $item, $patron );
461
        my $branch = Koha::Policy::Holds->holds_control_library( $item, $patron );
463
462
464
        my $policy_holdallowed = CanItemBeReserved( $patron, $item, undef, { get_from_cache => 1 } )->{status} eq 'OK'
463
        my $policy_holdallowed = CanItemBeReserved( $patron, $item, undef, { get_from_cache => 1 } )->{status} eq 'OK';
465
            && IsAvailableForItemLevelRequest( $item, $patron, undef );
466
464
467
        if ($policy_holdallowed) {
465
        if ($policy_holdallowed) {
468
            my $opac_hold_policy =
466
            my $opac_hold_policy =
(-)a/reserve/request.pl (-10 / +3 lines)
Lines 33-39 use Date::Calc qw( Date_to_Days ); Link Here
33
use C4::Output      qw( output_html_with_http_headers );
33
use C4::Output      qw( output_html_with_http_headers );
34
use C4::Auth        qw( get_template_and_user );
34
use C4::Auth        qw( get_template_and_user );
35
use C4::Reserves
35
use C4::Reserves
36
    qw( AlterPriority ToggleLowestPriority CanBookBeReserved GetMaxPatronHoldsForRecord CanItemBeReserved IsAvailableForItemLevelRequest );
36
    qw( AlterPriority ToggleLowestPriority CanBookBeReserved GetMaxPatronHoldsForRecord CanItemBeReserved );
37
use C4::Items       qw( get_hostitemnumbers_of );
37
use C4::Items       qw( get_hostitemnumbers_of );
38
use C4::Koha        qw( getitemtypeimagelocation );
38
use C4::Koha        qw( getitemtypeimagelocation );
39
use C4::Serials     qw( CountSubscriptionFromBiblionumber );
39
use C4::Serials     qw( CountSubscriptionFromBiblionumber );
Lines 395-401 if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) Link Here
395
            my $valid_items         = 0;
395
            my $valid_items         = 0;
396
396
397
            # iterating through all items first to check if any of them available
397
            # iterating through all items first to check if any of them available
398
            # to pass this value further inside down to IsAvailableForItemLevelRequest to
399
            # it's complicated logic to analyse.
398
            # it's complicated logic to analyse.
400
            # (before this loop was inside that sub loop so it was O(n^2) )
399
            # (before this loop was inside that sub loop so it was O(n^2) )
401
400
Lines 507-521 if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) Link Here
507
                        $default_pickup_branch = C4::Context->userenv->{branch};
506
                        $default_pickup_branch = C4::Context->userenv->{branch};
508
                    }
507
                    }
509
508
510
                    if (
509
                    if (   !$item->{cantreserve}
511
                           !$item->{cantreserve}
512
                        && !$exceeded_maxreserves
510
                        && !$exceeded_maxreserves
513
                        && $can_item_be_reserved eq 'OK'
511
                        && $can_item_be_reserved eq 'OK' )
514
515
                        # items_any_available defined outside of the current loop,
516
                        # so we avoiding loop inside IsAvailableForItemLevelRequest:
517
                        && IsAvailableForItemLevelRequest( $item_object, $patron, undef )
518
                        )
519
                    {
512
                    {
520
                        $valid_items = 1;
513
                        $valid_items = 1;
521
                    }
514
                    }
(-)a/t/db_dependent/Holds.t (-1 / +1 lines)
Lines 743-749 subtest 'CanItemBeReserved' => sub { Link Here
743
743
744
        is(
744
        is(
745
            CanItemBeReserved( $patrons[0], $item )->{status}, 'notforloan',
745
            CanItemBeReserved( $patrons[0], $item )->{status}, 'notforloan',
746
            "Item with no notforloan itemtype cannot be reserved"
746
            "Item with notforloan status cannot be reserved"
747
        );
747
        );
748
748
749
        $itemtype->notforloan(0)->store();
749
        $itemtype->notforloan(0)->store();
(-)a/t/db_dependent/Holds/DisallowHoldIfItemsAvailable.t (-55 / +63 lines)
Lines 16-22 use t::lib::TestBuilder; Link Here
16
use t::lib::Mocks;
16
use t::lib::Mocks;
17
17
18
BEGIN {
18
BEGIN {
19
    use_ok( 'C4::Reserves', qw( ItemsAnyAvailableAndNotRestricted IsAvailableForItemLevelRequest ) );
19
    use_ok( 'C4::Reserves', qw( ItemsAnyAvailableAndNotRestricted CanItemBeReserved ) );
20
}
20
}
21
21
22
my $schema = Koha::Database->schema;
22
my $schema = Koha::Database->schema;
Lines 35-47 my $itemtype = $builder->build( Link Here
35
    }
35
    }
36
)->{itemtype};
36
)->{itemtype};
37
37
38
t::lib::Mocks::mock_userenv( { branchcode => $library1->{branchcode} } );
38
my $library_A = $library1->{branchcode};
39
my $library_B = $library2->{branchcode};
40
41
t::lib::Mocks::mock_userenv( { branchcode => $library_A } );
39
42
40
my $patron1 = $builder->build_object(
43
my $patron1 = $builder->build_object(
41
    {
44
    {
42
        class => 'Koha::Patrons',
45
        class => 'Koha::Patrons',
43
        value => {
46
        value => {
44
            branchcode => $library1->{branchcode},
47
            branchcode => $library_A,
45
            dateexpiry => '3000-01-01',
48
            dateexpiry => '3000-01-01',
46
        }
49
        }
47
    }
50
    }
Lines 52-58 my $patron2 = $builder->build_object( Link Here
52
    {
55
    {
53
        class => 'Koha::Patrons',
56
        class => 'Koha::Patrons',
54
        value => {
57
        value => {
55
            branchcode => $library1->{branchcode},
58
            branchcode => $library_A,
56
            dateexpiry => '3000-01-01',
59
            dateexpiry => '3000-01-01',
57
        }
60
        }
58
    }
61
    }
Lines 62-76 my $patron3 = $builder->build_object( Link Here
62
    {
65
    {
63
        class => 'Koha::Patrons',
66
        class => 'Koha::Patrons',
64
        value => {
67
        value => {
65
            branchcode => $library2->{branchcode},
68
            branchcode => $library_B,
66
            dateexpiry => '3000-01-01',
69
            dateexpiry => '3000-01-01',
67
        }
70
        }
68
    }
71
    }
69
);
72
);
70
73
71
my $library_A = $library1->{branchcode};
72
my $library_B = $library2->{branchcode};
73
74
my $biblio       = $builder->build_sample_biblio( { itemtype => $itemtype } );
74
my $biblio       = $builder->build_sample_biblio( { itemtype => $itemtype } );
75
my $biblionumber = $biblio->biblionumber;
75
my $biblionumber = $biblio->biblionumber;
76
my $item1        = $builder->build_sample_item(
76
my $item1        = $builder->build_sample_item(
Lines 111-126 my $is; Link Here
111
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
111
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
112
is( $is, 1, "Items availability: both of 2 items are available" );
112
is( $is, 1, "Items availability: both of 2 items are available" );
113
113
114
$is = IsAvailableForItemLevelRequest( $item1, $patron1 );
114
$is = CanItemBeReserved( $patron1, $item1 );
115
is( $is, 0, "Item cannot be held, 2 items available" );
115
is( $is->{status}, "onShelfHoldsNotAllowed", "Item cannot be held, 2 items available" );
116
116
117
my $issue1 = AddIssue( $patron2, $item1->barcode );
117
my $issue1 = AddIssue( $patron2, $item1->barcode );
118
118
119
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
119
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
120
is( $is, 1, "Items availability: one item is available" );
120
is( $is, 1, "Items availability: one item is available" );
121
121
122
$is = IsAvailableForItemLevelRequest( $item1, $patron1 );
122
$is = CanItemBeReserved( $patron1, $item1 );
123
is( $is, 0, "Item cannot be held, 1 item available" );
123
is( $is->{status}, "onShelfHoldsNotAllowed", "Item cannot be held, 1 item available" );
124
124
125
AddIssue( $patron2, $item2->barcode );
125
AddIssue( $patron2, $item2->barcode );
126
$cache->flush();
126
$cache->flush();
Lines 128-140 $cache->flush(); Link Here
128
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
128
$is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron => $patron1 } );
129
is( $is, 0, "Items availability: none of items are available" );
129
is( $is, 0, "Items availability: none of items are available" );
130
130
131
$is = IsAvailableForItemLevelRequest( $item1, $patron1 );
131
$is = CanItemBeReserved( $patron1, $item1 );
132
is( $is, 1, "Item can be held, no items available" );
132
is( $is->{status}, "OK", "Item can be held, no items available" );
133
133
134
AddReturn( $item1->barcode );
134
AddReturn( $item1->barcode );
135
135
136
{    # Remove the issue for the first patron, and modify the branch for item1
136
{    # Remove the issue for the first patron, and modify the branch for item1
137
    subtest 'IsAvailableForItemLevelRequest behaviours depending on ReservesControlBranch + holdallowed' => sub {
137
    subtest 'CanItemBeReserved behaviours depending on ReservesControlBranch + holdallowed' => sub {
138
        plan tests => 2;
138
        plan tests => 2;
139
139
140
        my $hold_allowed_from_home_library  = 'from_home_library';
140
        my $hold_allowed_from_home_library  = 'from_home_library';
Lines 163-177 AddReturn( $item1->barcode ); Link Here
163
                    "Items availability: hold allowed from home + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
163
                    "Items availability: hold allowed from home + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
164
                );
164
                );
165
165
166
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
166
                $is = CanItemBeReserved( $patron1, $item1 );
167
                is(
167
                is(
168
                    $is, 1,
168
                    $is->{status}, "cannotReserveFromOtherBranches",
169
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
169
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
170
                        . "One item is available at different library, not holdable = none available => the hold is allowed at item level"
170
                        . "One item is available at different library, not holdable = none available => the hold is not allowed at item level"
171
                );
171
                );
172
                $is = IsAvailableForItemLevelRequest( $item1, $patron2 );
172
                $is = CanItemBeReserved( $patron2, $item1 );
173
                is(
173
                is(
174
                    $is, 1,
174
                    $is->{status}, "cannotReserveFromOtherBranches",
175
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
175
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
176
                        . "One item is available at home library, holdable = one available => the hold is not allowed at item level"
176
                        . "One item is available at home library, holdable = one available => the hold is not allowed at item level"
177
                );
177
                );
Lines 187-195 AddReturn( $item1->barcode ); Link Here
187
                    "Items availability: hold allowed from any library for library B + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
187
                    "Items availability: hold allowed from any library for library B + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
188
                );
188
                );
189
189
190
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
190
                $is = CanItemBeReserved( $patron1, $item1 );
191
                is(
191
                is(
192
                    $is, 0,
192
                    $is->{status}, "onShelfHoldsNotAllowed",
193
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
193
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
194
                        . "One item is available at different library, holdable = one available => the hold is not allowed at item level"
194
                        . "One item is available at different library, holdable = one available => the hold is not allowed at item level"
195
                );
195
                );
Lines 204-214 AddReturn( $item1->barcode ); Link Here
204
                    "Items availability: hold allowed from any library for library B + ReservesControlBranch=PatronLibrary + one item is available at different library"
204
                    "Items availability: hold allowed from any library for library B + ReservesControlBranch=PatronLibrary + one item is available at different library"
205
                );
205
                );
206
206
207
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
207
                $is = CanItemBeReserved( $patron1, $item1 );
208
                is(
208
                is(
209
                    $is, 1,
209
                    $is->{status}, "cannotReserveFromOtherBranches",
210
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
210
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
211
                        . "One item is available at different library, not holdable = none available => the hold is allowed at item level"
211
                        . "One item is available at different library, not holdable = none available => the hold is not allowed at item level"
212
                );
212
                );
213
213
214
                #Adding a rule for the patron's home library affects the availability for an item from another library because ReservesControlBranch is set to PatronLibrary
214
                #Adding a rule for the patron's home library affects the availability for an item from another library because ReservesControlBranch is set to PatronLibrary
Lines 222-230 AddReturn( $item1->barcode ); Link Here
222
                    "Items availability: hold allowed from any library for library A + ReservesControlBranch=PatronLibrary + one item is available at different library"
222
                    "Items availability: hold allowed from any library for library A + ReservesControlBranch=PatronLibrary + one item is available at different library"
223
                );
223
                );
224
224
225
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
225
                $is = CanItemBeReserved( $patron1, $item1 );
226
                is(
226
                is(
227
                    $is, 0,
227
                    $is->{status}, "onShelfHoldsNotAllowed",
228
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
228
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
229
                        . "One item is available at different library, holdable = one available => the hold is not allowed at item level"
229
                        . "One item is available at different library, holdable = one available => the hold is not allowed at item level"
230
                );
230
                );
Lines 243-251 AddReturn( $item1->barcode ); Link Here
243
                    "Items availability: hold allowed from any library + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
243
                    "Items availability: hold allowed from any library + ReservesControlBranch=ItemHomeLibrary + one item is available at different library"
244
                );
244
                );
245
245
246
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
246
                $is = CanItemBeReserved( $patron1, $item1 );
247
                is(
247
                is(
248
                    $is, 0,
248
                    $is->{status}, "onShelfHoldsNotAllowed",
249
                    "Hold allowed from any library + ReservesControlBranch=ItemHomeLibrary, "
249
                    "Hold allowed from any library + ReservesControlBranch=ItemHomeLibrary, "
250
                        . "One item is available at the diff library, holdable = 1 available => the hold is not allowed at item level"
250
                        . "One item is available at the diff library, holdable = 1 available => the hold is not allowed at item level"
251
                );
251
                );
Lines 260-268 AddReturn( $item1->barcode ); Link Here
260
                    "Items availability: hold allowed from any library + ReservesControlBranch=PatronLibrary + one item is available at different library"
260
                    "Items availability: hold allowed from any library + ReservesControlBranch=PatronLibrary + one item is available at different library"
261
                );
261
                );
262
262
263
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
263
                $is = CanItemBeReserved( $patron1, $item1 );
264
                is(
264
                is(
265
                    $is, 0,
265
                    $is->{status}, "onShelfHoldsNotAllowed",
266
                    "Hold allowed from any library + ReservesControlBranch=PatronLibrary, "
266
                    "Hold allowed from any library + ReservesControlBranch=PatronLibrary, "
267
                        . "One item is available at the diff library, holdable = 1 available => the hold is not allowed at item level"
267
                        . "One item is available at the diff library, holdable = 1 available => the hold is not allowed at item level"
268
                );
268
                );
Lines 294-302 AddReturn( $item1->barcode ); Link Here
294
                    "Items availability: hold allowed from home library + ReservesControlBranch=ItemHomeLibrary + one item is available at home library"
294
                    "Items availability: hold allowed from home library + ReservesControlBranch=ItemHomeLibrary + one item is available at home library"
295
                );
295
                );
296
296
297
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
297
                $is = CanItemBeReserved( $patron1, $item1 );
298
                is(
298
                is(
299
                    $is, 0,
299
                    $is->{status}, "onShelfHoldsNotAllowed",
300
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
300
                    "Hold allowed from home library + ReservesControlBranch=ItemHomeLibrary, "
301
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
301
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
302
                );
302
                );
Lines 310-318 AddReturn( $item1->barcode ); Link Here
310
                    "Items availability: hold allowed from home library + ReservesControlBranch=PatronLibrary + one item is available at home library"
310
                    "Items availability: hold allowed from home library + ReservesControlBranch=PatronLibrary + one item is available at home library"
311
                );
311
                );
312
312
313
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
313
                $is = CanItemBeReserved( $patron1, $item1 );
314
                is(
314
                is(
315
                    $is, 0,
315
                    $is->{status}, "onShelfHoldsNotAllowed",
316
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
316
                    "Hold allowed from home library + ReservesControlBranch=PatronLibrary, "
317
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
317
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
318
                );
318
                );
Lines 330-338 AddReturn( $item1->barcode ); Link Here
330
                    "Items availability: hold allowed from any library + ReservesControlBranch=ItemHomeLibrary + one item is available at home library"
330
                    "Items availability: hold allowed from any library + ReservesControlBranch=ItemHomeLibrary + one item is available at home library"
331
                );
331
                );
332
332
333
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
333
                $is = CanItemBeReserved( $patron1, $item1 );
334
                is(
334
                is(
335
                    $is, 0,
335
                    $is->{status}, "onShelfHoldsNotAllowed",
336
                    "Hold allowed from any library + ReservesControlBranch=ItemHomeLibrary, "
336
                    "Hold allowed from any library + ReservesControlBranch=ItemHomeLibrary, "
337
                        . "One item is available at the same library, holdable = 1 available => the hold is not allowed at item level"
337
                        . "One item is available at the same library, holdable = 1 available => the hold is not allowed at item level"
338
                );
338
                );
Lines 346-354 AddReturn( $item1->barcode ); Link Here
346
                    "Items availability: hold allowed from any library + ReservesControlBranch=PatronLibrary + one item is available at home library"
346
                    "Items availability: hold allowed from any library + ReservesControlBranch=PatronLibrary + one item is available at home library"
347
                );
347
                );
348
348
349
                $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
349
                $is = CanItemBeReserved( $patron1, $item1 );
350
                is(
350
                is(
351
                    $is, 0,
351
                    $is->{status}, "onShelfHoldsNotAllowed",
352
                    "Hold allowed from any library + ReservesControlBranch=PatronLibrary, "
352
                    "Hold allowed from any library + ReservesControlBranch=PatronLibrary, "
353
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
353
                        . "One item is available at the same library, holdable = 1 available  => the hold is not allowed at item level"
354
                );
354
                );
Lines 381-388 Koha::CirculationRules->set_rules( Link Here
381
        itemtype     => $itemtype2,
381
        itemtype     => $itemtype2,
382
        branchcode   => undef,
382
        branchcode   => undef,
383
        rules        => {
383
        rules        => {
384
            maxissueqty  => 99,
384
            reservesallowed => 99,
385
            onshelfholds => 0,
385
            maxissueqty     => 99,
386
            onshelfholds    => 0,
386
        }
387
        }
387
    }
388
    }
388
);
389
);
Lines 391-398 $is = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblionumber, patron Link Here
391
    ;    # patron1 in library A, library A 1 item
392
    ;    # patron1 in library A, library A 1 item
392
is( $is, 1, "Items availability: 1 item is available, 1 item held in T" );
393
is( $is, 1, "Items availability: 1 item is available, 1 item held in T" );
393
394
394
$is = IsAvailableForItemLevelRequest( $item3, $patron1 );
395
$is = CanItemBeReserved( $patron1, $item3 );
395
is( $is, 1, "Item can be held, items in transit are not available" );
396
is( $is->{status}, "OK", "Item can be held, items in transit are not available" );
396
397
397
subtest 'Check holds availability with different item types' => sub {
398
subtest 'Check holds availability with different item types' => sub {
398
    plan tests => 6;
399
    plan tests => 6;
Lines 459-469 subtest 'Check holds availability with different item types' => sub { Link Here
459
        "Items availability: 2 items, one allowed by smart rule but not checked out, another one not allowed to be held by smart rule"
460
        "Items availability: 2 items, one allowed by smart rule but not checked out, another one not allowed to be held by smart rule"
460
    );
461
    );
461
462
462
    $is = IsAvailableForItemLevelRequest( $item4, $patron1 );
463
    $is = CanItemBeReserved( $patron1, $item4 );
463
    is( $is, 0, "Item4 cannot be requested to hold: 2 items, Item4 available, Item5 restricted" );
464
    is(
465
        $is->{status}, "onShelfHoldsNotAllowed",
466
        "Item4 cannot be requested to hold: 2 items, Item4 available, Item5 restricted"
467
    );
464
468
465
    $is = IsAvailableForItemLevelRequest( $item5, $patron1 );
469
    $is = CanItemBeReserved( $patron1, $item5 );
466
    is( $is, 0, "Item5 cannot be requested to hold: 2 items, Item4 available, Item5 restricted" );
470
    is(
471
        $is->{status}, "noReservesAllowed",
472
        "Item5 cannot be requested to hold: 2 items, Item4 available, Item5 restricted"
473
    );
467
474
468
    AddIssue( $patron2, $item4->barcode );
475
    AddIssue( $patron2, $item4->barcode );
469
    $cache->flush();
476
    $cache->flush();
Lines 474-486 subtest 'Check holds availability with different item types' => sub { Link Here
474
        "Items availability: 2 items, one allowed by smart rule and checked out, another one not allowed to be held by smart rule"
481
        "Items availability: 2 items, one allowed by smart rule and checked out, another one not allowed to be held by smart rule"
475
    );
482
    );
476
483
477
    $is = IsAvailableForItemLevelRequest( $item4, $patron1 );
484
    $is = CanItemBeReserved( $patron1, $item4 );
478
    is( $is, 1, "Item4 can be requested to hold, 2 items, Item4 checked out, Item5 restricted" );
485
    is( $is->{status}, "OK", "Item4 can be requested to hold, 2 items, Item4 checked out, Item5 restricted" );
479
486
480
    $is = IsAvailableForItemLevelRequest( $item5, $patron1 );
487
    $is = CanItemBeReserved( $patron1, $item5 );
481
488
    is(
482
    # Note: read IsAvailableForItemLevelRequest sub description about CanItemBeReserved/CanBookBeReserved:
489
        $is->{status}, "noReservesAllowed",
483
    is( $is, 1, "Item5 can be requested to hold, 2 items, Item4 checked out, Item5 restricted" );
490
        "Item5 can be requested to hold, 2 items, Item4 checked out, Item5 restricted"
491
    );
484
};
492
};
485
493
486
subtest 'Check item checkout availability with ordered item' => sub {
494
subtest 'Check item checkout availability with ordered item' => sub {
Lines 549-556 subtest 'Check item availability for hold with ordered item' => sub { Link Here
549
    );
557
    );
550
558
551
    $cache->flush();
559
    $cache->flush();
552
    $is = IsAvailableForItemLevelRequest( $item1, $patron1 );
560
    $is = CanItemBeReserved( $patron1, $item1 );
553
    is( $is, 1, "Ordered items are available for hold" );
561
    is( $is->{status}, "OK", "Ordered items are available for hold" );
554
};
562
};
555
563
556
# Cleanup
564
# Cleanup
(-)a/t/db_dependent/Reserves.t (-54 / +15 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 71;
20
use Test::More tests => 70;
21
use Test::NoWarnings;
21
use Test::NoWarnings;
22
use Test::MockModule;
22
use Test::MockModule;
23
use Test::Warn;
23
use Test::Warn;
Lines 34-40 use C4::Biblio qw( GetMarcFromKohaField ModBiblio ); Link Here
34
use C4::HoldsQueue;
34
use C4::HoldsQueue;
35
use C4::Members;
35
use C4::Members;
36
use C4::Reserves
36
use C4::Reserves
37
    qw( AddReserve AlterPriority CheckReserves ModReserve ModReserveAffect ReserveSlip CalculatePriority CanBookBeReserved IsAvailableForItemLevelRequest MoveReserve ChargeReserveFee CanItemBeReserved MergeHolds );
37
    qw( AddReserve AlterPriority CheckReserves ModReserve ModReserveAffect ReserveSlip CalculatePriority CanBookBeReserved MoveReserve ChargeReserveFee CanItemBeReserved MergeHolds );
38
use Koha::ActionLogs;
38
use Koha::ActionLogs;
39
use Koha::Biblios;
39
use Koha::Biblios;
40
use Koha::Caches;
40
use Koha::Caches;
Lines 659-667 is( Link Here
659
####### EO Bug 13113 <<<
659
####### EO Bug 13113 <<<
660
####
660
####
661
661
662
ok( C4::Reserves::IsAvailableForItemLevelRequest( $item, $patron ), "Reserving a book on item level" );
662
is( C4::Reserves::CanItemBeReserved( $patron, $item )->{status}, "OK", "Reserving a book on item level" );
663
663
my $branch = $builder->build_object(
664
my $pickup_branch = $builder->build( { source => 'Branch' } )->{branchcode};
664
    {
665
        class => 'Koha::Libraries',
666
        value => {
667
            branchemail     => 'branch@e.mail',
668
            branchreplyto   => 'branch@reply.to',
669
            pickup_location => 1
670
        }
671
    }
672
);
673
my $pickup_branch = $branch->branchcode;
665
t::lib::Mocks::mock_preference( 'UseBranchTransferLimits',  '1' );
674
t::lib::Mocks::mock_preference( 'UseBranchTransferLimits',  '1' );
666
t::lib::Mocks::mock_preference( 'BranchTransferLimitsType', 'itemtype' );
675
t::lib::Mocks::mock_preference( 'BranchTransferLimitsType', 'itemtype' );
667
my $limit = Koha::Item::Transfer::Limit->new(
676
my $limit = Koha::Item::Transfer::Limit->new(
Lines 672-678 my $limit = Koha::Item::Transfer::Limit->new( Link Here
672
    }
681
    }
673
)->store();
682
)->store();
674
is(
683
is(
675
    C4::Reserves::IsAvailableForItemLevelRequest( $item, $patron, $pickup_branch ), 0,
684
    C4::Reserves::CanItemBeReserved( $patron, $item, $pickup_branch )->{status}, "cannotBeTransferred",
676
    "Item level request not available due to transfer limit"
685
    "Item level request not available due to transfer limit"
677
);
686
);
678
t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '0' );
687
t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '0' );
Lines 1534-1586 sub place_item_hold { Link Here
1534
# we reached the finish
1543
# we reached the finish
1535
$schema->storage->txn_rollback();
1544
$schema->storage->txn_rollback();
1536
1545
1537
subtest 'IsAvailableForItemLevelRequest() tests' => sub {
1538
1539
    plan tests => 2;
1540
1541
    $schema->storage->txn_begin;
1542
1543
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
1544
1545
    my $item_type = undef;
1546
1547
    my $item_mock = Test::MockModule->new('Koha::Item');
1548
    $item_mock->mock( 'effective_itemtype', sub { return $item_type; } );
1549
1550
    my $item = $builder->build_sample_item;
1551
1552
    # Weird use case to highlight issue
1553
    $item_type = '0';
1554
    Koha::ItemTypes->search( { itemtype => $item_type } )->delete;
1555
    my $itemtype = $builder->build_object(
1556
        {
1557
            class => 'Koha::ItemTypes',
1558
            value => { itemtype => $item_type }
1559
        }
1560
    );
1561
    ok(
1562
        C4::Reserves::IsAvailableForItemLevelRequest( $item, $patron ),
1563
        "Item not available for item-level hold because no effective item type"
1564
    );
1565
1566
    Koha::CirculationRules->set_rules(
1567
        {
1568
            categorycode => '*',
1569
            itemtype     => '*',
1570
            branchcode   => '*',
1571
            rules        => {
1572
                onshelfholds => 0,
1573
            }
1574
        }
1575
    );
1576
    my $item_1 = $builder->build_sample_item( { notforloan => -1 } );
1577
    ok(
1578
        C4::Reserves::IsAvailableForItemLevelRequest( $item_1, $patron ),
1579
        "We can placing hold on item with negative not for loan values when 'On shelf holds allowed' is set to 'If any unavailable'"
1580
    );
1581
    $schema->storage->txn_rollback;
1582
};
1583
1584
subtest 'AddReserve() tests' => sub {
1546
subtest 'AddReserve() tests' => sub {
1585
1547
1586
    plan tests => 2;
1548
    plan tests => 2;
1587
- 

Return to bug 32702