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

(-)a/Koha/Availability/Search.pm (+47 lines)
Line 0 Link Here
1
package Koha::Availability::Search;
2
3
# Copyright Koha-Suomi Oy 2016
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Koha::Biblio::Availability::Search;
23
use Koha::Item::Availability::Search;
24
25
use Koha::Exceptions;
26
27
sub new {
28
    my ($class, $params) = @_;
29
30
    my $self = {};
31
32
    bless $self, $class;
33
}
34
35
sub biblio {
36
    my ($self, $params) = @_;
37
38
    return Koha::Biblio::Availability::Search->new($params);
39
}
40
41
sub item {
42
    my ($self, $params) = @_;
43
44
    return Koha::Item::Availability::Search->new($params);
45
}
46
47
1;
(-)a/Koha/Biblio/Availability/Search.pm (+177 lines)
Line 0 Link Here
1
package Koha::Biblio::Availability::Search;
2
3
# Copyright Koha-Suomi Oy 2016
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw(Koha::Biblio::Availability);
23
24
use Koha::Item::Availability::Search;
25
26
=head1 NAME
27
28
Koha::Biblio::Availability::Search - Koha Biblio Availability Search object class
29
30
=head1 SYNOPSIS
31
32
my $searchability = Koha::Biblio::Availability::Search->new({
33
    biblio => $biblio,      # which biblio this availability is for
34
})
35
36
=head1 DESCRIPTION
37
38
Class for checking biblio search availability.
39
40
This class contains subroutines to determine biblio's availability for search
41
result in different contexts.
42
43
=head2 Class Methods
44
45
=cut
46
47
=head3 new
48
49
Constructs an biblio search availability object. Biblio is always required.
50
51
MANDATORY PARAMETERS
52
53
    biblio (or biblionumber)
54
55
Biblio is a Koha::Biblio -object.
56
57
OPTIONAL PARAMETERS
58
59
Returns a Koha::Biblio::Availability::Search -object.
60
61
=cut
62
63
sub new {
64
    my ($class, $params) = @_;
65
66
    my $self = $class->SUPER::new($params);
67
68
    return $self;
69
}
70
71
sub in_intranet {
72
    my ($self, $params) = @_;
73
74
    $self->reset;
75
76
    # Item looper
77
    $self->_item_looper($params);
78
79
    return $self;
80
}
81
82
sub in_opac {
83
    my ($self, $params) = @_;
84
85
    $self->reset;
86
87
    $params->{'opac'} = 1;
88
89
    # Item looper
90
    $self->_item_looper($params);
91
92
    return $self;
93
}
94
95
sub _item_looper {
96
    my ($self, $params) = @_;
97
98
    my @items = $self->biblio->items;
99
    my @hostitemnumbers = C4::Items::get_hostitemnumbers_of($self->biblio->biblionumber);
100
    if (@hostitemnumbers) {
101
        my @hostitems = Koha::Items->search({
102
            itemnumber => { 'in' => @hostitemnumbers }
103
        });
104
        push @items, @hostitems;
105
    }
106
107
    if (scalar(@items) == 0) {
108
        $self->unavailable(Koha::Exceptions::Biblio::NoAvailableItems->new);
109
        return;
110
    }
111
112
    my $opac = $params->{'opac'} ? 1:0;
113
    my $hidelostitems = 0;
114
    if (!$opac) {
115
        $hidelostitems = C4::Context->preference('hidelostitems');
116
    }
117
118
    # Stop calculating item availabilities after $limit available items are found.
119
    # E.g. parameter 'limit' with value 1 will find only one available item and
120
    # return biblio as available if no other unavailabilities are found. If you
121
    # want to calculate availability of every item in this biblio, do not give this
122
    # parameter.
123
    my $limit = $params->{'limit'};
124
    my $avoid_queries_after =
125
    C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck');
126
    my $count = 0;
127
    foreach my $item (@items) {
128
        # Break out of loop after $limit items are found available
129
        if (defined $limit && @{$self->{'item_availabilities'}} >= $limit) {
130
            last;
131
        }
132
133
        my $item_availability = Koha::Item::Availability::Search->new({
134
            item => $item,
135
        });
136
        if ($count >= $avoid_queries_after) {
137
            # A couple heuristics to limit how many times
138
            # we query the database for item transfer information, sacrificing
139
            # accuracy in some cases for speed;
140
            #
141
            # 1. don't query if item has one of the other statuses (done inside
142
            #    item availability calculation)
143
            # 2. don't check holds status if ignore_holds parameter is given
144
            # 3. don't check transfer status if ignore_transfer parameter is given
145
            $params->{'ignore_holds'} = 1;
146
            $params->{'ignore_transfer'} = 1;
147
        }
148
        if ($opac) {
149
            $item_availability = $item_availability->in_opac($params);
150
            my $unavails = $item_availability->unavailabilities;
151
            # Hide item in OPAC context if system preference hidelostitems is
152
            # enabled.
153
            my $lost = exists $unavails->{'Koha::Exceptions::Item::Lost'};
154
            if ($hidelostitems && $lost) {
155
                next;
156
            }
157
        } else {
158
            $item_availability = $item_availability->in_intranet($params);
159
        }
160
        if ($item_availability->available) {
161
            push @{$self->{'item_availabilities'}}, $item_availability;
162
        } else {
163
            push @{$self->{'item_unavailabilities'}}, $item_availability;
164
        }
165
        $count++;
166
    }
167
168
    # After going through items, if none are found available, set the biblio
169
    # unavailable
170
    if (@{$self->{'item_availabilities'}} == 0) {
171
        $self->unavailable(Koha::Exceptions::Biblio::NoAvailableItems->new);
172
    }
173
174
    return $self;
175
}
176
177
1;
(-)a/Koha/Item/Availability/Search.pm (+76 lines)
Line 0 Link Here
1
package Koha::Item::Availability::Search;
2
3
# Copyright Koha-Suomi Oy 2016
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw(Koha::Item::Availability);
23
24
use Koha::Availability::Checks::Item;
25
26
sub new {
27
    my ($class, $params) = @_;
28
29
    my $self = $class->SUPER::new($params);
30
31
    return $self;
32
}
33
34
sub in_opac {
35
    my ($self, $params) = @_;
36
    my $reason;
37
38
    $self->reset;
39
40
    my $item = $self->item;
41
42
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
43
    $self->unavailable($reason) if $reason = $itemcalc->lost;
44
    $self->unavailable($reason) if $reason = $itemcalc->damaged;
45
    $self->unavailable($reason) if $reason = $itemcalc->from_another_library;
46
    $self->unavailable($reason) if $reason = $itemcalc->notforloan;
47
    $self->unavailable($reason) if $reason = $itemcalc->restricted;
48
    $self->unavailable($reason) if $reason = $itemcalc->unknown_barcode;
49
    $self->unavailable($reason) if $reason = $itemcalc->withdrawn;
50
    if ($itemcalc->onloan) {
51
        $self->unavailable($reason) if $reason = $itemcalc->checked_out;
52
    }
53
54
    if (!$params->{'ignore_holds'} && $self->available) {
55
        $self->unavailable($reason) if $reason = $itemcalc->held;
56
    }
57
    if (!$params->{'ignore_transfer'} && $self->available) {
58
        $self->unavailable($reason) if $reason = $itemcalc->transfer;
59
    }
60
61
    return $self;
62
}
63
64
sub in_intranet {
65
    my ($self, $params) = @_;
66
67
    # TODO:
68
    # Do we want to show some additional information in intranet context?
69
    # Perhaps make some additional checks, or override some unavailabilities
70
    # found in OPAC.
71
    #
72
    # For now, just run the same checks as OPAC context.
73
    return $self->in_opac($params);
74
}
75
76
1;
(-)a/t/db_dependent/Koha/Biblio/Availability/Search.t (+164 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright Koha-Suomi Oy 2016
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More tests => 4;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
use Benchmark;
26
27
use Koha::Database;
28
use Koha::IssuingRules;
29
use Koha::Items;
30
use Koha::ItemTypes;
31
32
use Koha::Availability::Search;
33
use Koha::Biblio::Availability::Search;
34
35
my $schema = Koha::Database->new->schema;
36
$schema->storage->txn_begin;
37
38
my $builder = t::lib::TestBuilder->new;
39
40
set_default_system_preferences();
41
set_default_circulation_rules();
42
43
subtest 'Biblio with zero available items' => \&t_no_available_items;
44
sub t_no_available_items {
45
    plan tests => 6;
46
47
    my $item1 = build_a_test_item();
48
    my $biblio = Koha::Biblios->find($item1->biblionumber);
49
    my $item2 = build_a_test_item();
50
    $item2->biblionumber($biblio->biblionumber)->store;
51
    $item2->biblioitemnumber($item1->biblioitemnumber)->store;
52
    $item1->withdrawn('1')->store;
53
    $item2->notforloan('1')->store;
54
    my $patron = build_a_test_patron();
55
56
    my $availability = Koha::Biblio::Availability::Search->new({
57
        biblio => $biblio,
58
    })->in_opac;
59
    my $expecting = 'Koha::Exceptions::Biblio::NoAvailableItems';
60
61
    ok(!Koha::Item::Availability::Search->new({
62
            item => $item1,
63
        })->in_opac->available,
64
       'When I look at the first item of two in this biblio, it is not available.');
65
    ok(!Koha::Item::Availability::Search->new({
66
            item => $item2
67
        })->in_opac->available,
68
       'When I look at the second item of two in this biblio, it is not available.');
69
    ok(!$availability->available, 'Then, the biblio is not available.');
70
    is($availability->unavailable, 1, 'Then, there is exactly one reason for unavailability.');
71
    is(ref($availability->unavailabilities->{$expecting}), $expecting, 'The reason says there are no'
72
       .' available items in this biblio.');
73
    is(@{$availability->item_unavailabilities}, 2, 'There seems to be two items that are unavailable.');
74
};
75
76
subtest 'Biblio with one available items out of two' => \&t_one_out_of_two_items_available;
77
sub t_one_out_of_two_items_available {
78
    plan tests => 5;
79
80
    my $item1 = build_a_test_item();
81
    my $biblio = Koha::Biblios->find($item1->biblionumber);
82
    my $item2 = build_a_test_item();
83
    $item2->biblionumber($biblio->biblionumber)->store;
84
    $item2->biblioitemnumber($item1->biblioitemnumber)->store;
85
86
    my $patron = build_a_test_patron();
87
    $item1->withdrawn('1')->store;
88
89
    my $availability = Koha::Biblio::Availability::Search->new({biblio => $biblio})->in_opac;
90
    my $item_availabilities = $availability->item_availabilities;
91
    ok(!Koha::Item::Availability::Search->new({ item => $item1,})->in_opac->available,
92
       'When I look at the first item of two in this biblio, it is not available.');
93
    ok(Koha::Item::Availability::Search->new({ item => $item2,})->in_opac->available,
94
       'When I look at the second item of two in this biblio, it seems to be available.');
95
    ok($availability->available, 'Then, the biblio is available.');
96
    is(@{$item_availabilities}, 1, 'There seems to be one available item in this biblio.');
97
    is($item_availabilities->[0]->item->itemnumber, $item2->itemnumber, 'Then the only available item'
98
       .' is the second item of this biblio.');
99
};
100
101
subtest 'Biblio with two items out of two available' => \&t_all_items_available;
102
sub t_all_items_available {
103
    plan tests => 4;
104
105
    my $item1 = build_a_test_item();
106
    my $biblio = Koha::Biblios->find($item1->biblionumber);
107
    my $item2 = build_a_test_item();
108
    $item2->biblionumber($biblio->biblionumber)->store;
109
    $item2->biblioitemnumber($item1->biblioitemnumber)->store;
110
111
    my $patron = build_a_test_patron();
112
113
    my $availability = Koha::Biblio::Availability::Search->new({biblio => $biblio})->in_opac;
114
    my $item_availabilities = $availability->item_availabilities;
115
    ok(Koha::Item::Availability::Search->new({ item => $item1,})->in_opac->available,
116
       'When I look at the first item of two in this biblio, it seems to be available.');
117
    ok(Koha::Item::Availability::Search->new({ item => $item2,})->in_opac->available,
118
       'When I look at the second item of two in this biblio, it seems to be available.');
119
    ok($availability->available, 'Then, the biblio is available.');
120
    is(@{$item_availabilities}, 2, 'There seems to be two available items in this biblio.');
121
};
122
123
subtest 'Prove that lower MaxSearchResultsItemsPerRecordStatusCheck boosts performance' => \&t_performance_proof;
124
sub t_performance_proof {
125
    plan tests => 6;
126
127
    my $item = build_a_test_item();
128
    my $biblio = Koha::Biblios->find($item->biblionumber);
129
    my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
130
131
    # add some items to biblio
132
    my $count1 = 20;
133
    my $count2 = 200;
134
    for my $i (1..($count2-1)) { # one already built earlier
135
        build_a_test_item($biblio, $biblioitem);
136
    }
137
138
    my $availability_iterations = 10;
139
    my @items = $biblio->items;
140
    is(@items, $count2, "We will get availability for $count2 items.");
141
    t::lib::Mocks::mock_preference('MaxSearchResultsItemsPerRecordStatusCheck',
142
                                    $count1);
143
    is(C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck'),
144
       $count1, "First test will be done with"
145
       ." MaxSearchResultsItemsPerRecordStatusCheck = $count1");
146
    my $res1 = timethis($availability_iterations, sub {
147
        Koha::Availability::Search->new->biblio({ biblio => $biblio })->in_opac;
148
    });
149
    ok($res1, "Calculated search availability $availability_iterations times.");
150
    t::lib::Mocks::mock_preference('MaxSearchResultsItemsPerRecordStatusCheck',
151
                                    $count2);
152
    is(C4::Context->preference('MaxSearchResultsItemsPerRecordStatusCheck'),
153
       $count2, "Second test will be done with"
154
       ." MaxSearchResultsItemsPerRecordStatusCheck = $count2");
155
    my $res2 = timethis($availability_iterations, sub {
156
        Koha::Availability::Search->new->biblio({ biblio => $biblio })->in_opac;
157
    });
158
    ok($res2, "Calculated search availability $availability_iterations times.");
159
    ok($res1->cpu_a < $res2->cpu_a, 'First test was faster than second.');
160
};
161
162
$schema->storage->txn_rollback;
163
164
1;
(-)a/t/db_dependent/Koha/Item/Availability/Search.t (-1 / +266 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright Koha-Suomi Oy 2016
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope t1hat it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More tests => 10;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
26
use Koha::Database;
27
use Koha::IssuingRules;
28
use Koha::Items;
29
use Koha::ItemTypes;
30
31
use Koha::Item::Availability::Search;
32
33
my $schema = Koha::Database->new->schema;
34
$schema->storage->txn_begin;
35
36
my $builder = t::lib::TestBuilder->new;
37
38
set_default_system_preferences();
39
set_default_circulation_rules();
40
41
subtest 'Given item is in a good state for availability' => \&t_ok_availability;
42
sub t_ok_availability {
43
    plan tests => 3;
44
45
    my $patron = build_a_test_patron();
46
    my $item = build_a_test_item();
47
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
48
49
    ok($availability->available, 'When I request availability, then the item is available.');
50
    ok(!$availability->confirm, 'Then nothing needs to be confirmed.');
51
    ok(!$availability->unavailable, 'Then there are no reasons to be unavailable.');
52
}
53
54
subtest 'Given item is damaged' => sub {
55
    plan tests => 2;
56
57
    subtest 'Given AllowHoldsOnDamagedItems is disabled' => \&t_damaged_item_allow_disabled;
58
    subtest 'Given AllowHoldsOnDamagedItems is enabled' => \&t_damaged_item_allow_enabled;
59
    sub t_damaged_item_allow_disabled {
60
        plan tests => 4;
61
62
        t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0);
63
64
        my $patron = build_a_test_patron();
65
        my $item = build_a_test_item()->set({damaged=>1})->store;
66
        my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
67
        my $expecting = 'Koha::Exceptions::Item::Damaged';
68
69
        is($item->damaged, 1, 'When I look at the item, I see that it is damaged.');
70
        ok(!$availability->available, 'When I request availability, then the item is not available.');
71
        is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
72
        is(ref($availability->unavailabilities->{$expecting}), $expecting,
73
            'Then there is an unavailability status indicating damaged item.');
74
    };
75
    sub t_damaged_item_allow_enabled {
76
        plan tests => 4;
77
78
        t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 1);
79
80
        my $patron = build_a_test_patron();
81
        my $item = build_a_test_item()->set({damaged=>1})->store;
82
        my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
83
84
        is($item->damaged, 1, 'When I look at the item, I see that it is damaged.');
85
        ok($availability->available, 'When I request availability, then the item is available.');
86
        ok(!$availability->unavailable, 'Then there are no statuses for unavailability.');
87
        ok(!$availability->confirm, 'Then there is no reason to have availability confirmed.');
88
    };
89
};
90
91
subtest 'Given item is lost' => \&t_lost;
92
sub t_lost {
93
    plan tests => 4;
94
95
    my $patron = build_a_test_patron();
96
    my $item = build_a_test_item()->set({itemlost=>1})->store;
97
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
98
    my $expecting = 'Koha::Exceptions::Item::Lost';
99
100
    is($item->itemlost, 1, 'When I try to look at the item, I find out that it is lost.');
101
    ok(!$availability->available, 'When I request availability, then the item is not available.');
102
    is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
103
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
104
        'Then there is an unavailability status indicating lost item.');
105
};
106
107
subtest 'Given item is not for loan' => \&t_notforloan;
108
sub t_notforloan {
109
    plan tests => 4;
110
111
    my $patron = build_a_test_patron();
112
    my $item = build_a_test_item()->set({notforloan=>1})->store;
113
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
114
    my $expecting = 'Koha::Exceptions::Item::NotForLoan';
115
116
    is($item->notforloan, 1, 'When I look at the item, I see that it is not for loan.');
117
    ok(!$availability->available, 'When I request availability, then the item is not available.');
118
    is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
119
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
120
        'Then there is an unavailability status indicating the item is not for loan.');
121
};
122
123
subtest 'Given item type is not for loan' => sub {
124
125
    subtest 'Given item-level_itypes is on (item-itemtype)' => \&t_itemlevel_itemtype_notforloan_item_level_itypes_on;
126
    subtest 'Given item-level_itypes is off (biblioitem-itemtype)' => \&t_itemlevel_itemtype_notforloan_item_level_itypes_off;
127
    sub t_itemlevel_itemtype_notforloan_item_level_itypes_on {
128
        plan tests => 5;
129
130
        t::lib::Mocks::mock_preference('item-level_itypes', 1);
131
132
        my $patron = build_a_test_patron();
133
        my $item = build_a_test_item();
134
        my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
135
        my $itemtype = Koha::ItemTypes->find($item->itype);
136
        $itemtype->set({notforloan=>1})->store;
137
        my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
138
        my $expecting = 'Koha::Exceptions::ItemType::NotForLoan';
139
140
        is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 0, 'Biblioitem itemtype is for loan.');
141
        is(Koha::ItemTypes->find($item->itype)->notforloan, 1, 'Item itemtype is not for loan.');
142
        ok(!$availability->available, 'When I request availability, then the item is not available.');
143
        is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
144
        is(ref($availability->unavailabilities->{$expecting}), $expecting,
145
            "Then there is an unavailability status indicating the itemtype is not forloan.");
146
    };
147
    sub t_itemlevel_itemtype_notforloan_item_level_itypes_off {
148
        plan tests => 5;
149
150
        t::lib::Mocks::mock_preference('item-level_itypes', 0);
151
152
        my $patron = build_a_test_patron();
153
        my $item = build_a_test_item();
154
        my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
155
        my $itemtype = Koha::ItemTypes->find($biblioitem->itemtype);
156
        $itemtype->set({notforloan=>1})->store;
157
        my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
158
        my $expecting = 'Koha::Exceptions::ItemType::NotForLoan';
159
160
        is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 1, 'Biblioitem itemtype is not for loan.');
161
        is(Koha::ItemTypes->find($item->itype)->notforloan, 0, 'Item itemtype is for loan.');
162
        ok(!$availability->available, 'When I request availability, then the item is not available.');
163
        is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
164
        is(ref($availability->unavailabilities->{$expecting}), $expecting,
165
            "Then there is an unavailability status indicating the itemtype is not forloan.");
166
    };
167
};
168
169
subtest 'Given item is ordered' => \&t_ordered;
170
sub t_ordered {
171
    plan tests => 4;
172
173
    my $patron = build_a_test_patron();
174
    my $item = build_a_test_item()->set({notforloan=>-1})->store;
175
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
176
    my $expecting = 'Koha::Exceptions::Item::NotForLoan';
177
178
    ok(!$availability->available, 'When I request availability, then the item is not available.');
179
    ok($availability->unavailable, 'Then there is one reason for unavailability.');
180
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
181
        'Then the unavailability status is indicating not for loan status.');
182
    is($availability->unavailabilities->{$expecting}->code, 'Ordered', 'Not for loan code says the item is ordered.')
183
};
184
185
subtest 'Given item is restricted' => \&t_restricted;
186
sub t_restricted {
187
    plan tests => 4;
188
189
    my $patron = build_a_test_patron();
190
    my $item = build_a_test_item()->set({restricted=>1})->store;
191
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
192
    my $expecting = 'Koha::Exceptions::Item::Restricted';
193
194
    is($item->restricted, 1, 'When I look at the item, I see that it is restricted.');
195
    ok(!$availability->available, 'When I request availability, then the item is not available.');
196
    is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
197
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
198
        'Then there is an unavailability status indicating restricted item.');
199
};
200
201
subtest 'Given item has no barcode' => \&t_unknown_barcode;
202
sub t_unknown_barcode {
203
    plan tests => 4;
204
205
    my $patron = build_a_test_patron();
206
    my $item = build_a_test_item()->set({barcode=>undef})->store;
207
    my $expecting = 'Koha::Exceptions::Item::UnknownBarcode';
208
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
209
210
    is($item->barcode, undef, 'When I look at the item, we see that it has undefined barcode.');
211
    ok($availability->unavailable, 'When I request availability, then the item is not available.');
212
    is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
213
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
214
       'Then there is an unavailability status indicating unknown barcode.');
215
};
216
217
subtest 'Given item is withdrawn' => \&t_withdrawn;
218
sub t_withdrawn {
219
    plan tests => 4;
220
221
    my $patron = build_a_test_patron();
222
    my $item = build_a_test_item()->set({restricted=>1})->store;
223
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
224
    my $expecting = 'Koha::Exceptions::Item::Restricted';
225
226
    is($item->restricted, 1, 'When I look at the item, I see that it is restricted.');
227
    ok(!$availability->available, 'When I request availability, then the item is not available.');
228
    is($availability->unavailable, 1, 'Then there is only one unavailability reason.');
229
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
230
        'Then there is an unavailability status indicating restricted item.');
231
};
232
233
subtest 'Held' => \&t_held;
234
sub t_held {
235
    plan tests => 8;
236
237
    my $patron = build_a_test_patron();
238
    my $item = build_a_test_item();
239
    my $reserve_id = add_item_level_hold($item, $patron, $item->homebranch);
240
    my $hold = Koha::Holds->find({ borrowernumber => $patron->borrowernumber });
241
    Koha::IssuingRules->search->delete;
242
    my $rule = Koha::IssuingRule->new({
243
        branchcode   => $item->homebranch,
244
        itemtype     => $item->effective_itemtype,
245
        categorycode => $patron->categorycode,
246
        holds_per_record => 9001,
247
        reservesallowed => 9001,
248
        opacitemholds => 'Y',
249
    })->store;
250
    my $availability = Koha::Item::Availability::Search->new({item => $item})->in_opac;
251
    my $expecting = 'Koha::Exceptions::Item::Held';
252
253
    is($rule->reservesallowed, 9001, 'As I look at circulation rules, I can see that many reserves are allowed.');
254
    ok($reserve_id, 'I have placed a hold on an item.');
255
    is($hold->itemnumber, $item->itemnumber, 'The item I have hold for is the same item I will check availability for.');
256
    ok(!$availability->available, 'When I request availability, then the item is not available.');
257
    ok(!$availability->confirm, 'Then there is nothing to be confirmed.');
258
    ok(!$availability->note, 'Then there are no additional notes.');
259
    is($availability->unavailable, 1, 'Then there is only one reason for unavailability.');
260
    is(ref($availability->unavailabilities->{$expecting}), $expecting,
261
        'Then there is an unavailability status indicating that I have already held this.');
262
};
263
264
$schema->storage->txn_rollback;
265
266
1;

Return to bug 17712