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

(-)a/Koha/Availability/Checks.pm (+57 lines)
Line 0 Link Here
1
package Koha::Availability::Checks;
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::Exceptions;
23
use Koha::Exceptions::Object;
24
25
sub new {
26
    my $class = shift;
27
28
    my $self = {};
29
    bless $self, $class;
30
31
    return $self;
32
}
33
34
sub AUTOLOAD {
35
    my ($self, $params) = @_;
36
37
    my $method = our $AUTOLOAD;
38
    $method =~ s/.*://;
39
40
    # Accessor for class parameters
41
    if (exists $self->{$method}) {
42
        unless (defined $params) {
43
            return $self->{$method};
44
        } else {
45
            $self->{$method} = $params;
46
            return $self;
47
        }
48
    } elsif ($self->can($method)) {
49
        $self->$method($params);
50
    } else {
51
        Koha::Exceptions::Object::MethodNotFound->throw(
52
            "No method $method for " . ref($self)
53
        );
54
    }
55
}
56
57
1;
(-)a/Koha/Availability/Checks/Biblio.pm (+116 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::Biblio;
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::Availability::Checks);
23
24
use C4::Context;
25
use C4::Circulation;
26
use C4::Serials;
27
28
use Koha::Biblios;
29
use Koha::Checkouts;
30
use Koha::Patrons;
31
32
use Koha::Exceptions::Biblio;
33
34
sub new {
35
    my ($class, $biblio) = @_;
36
37
    unless ($biblio) {
38
        Koha::Exceptions::MissingParameter->throw(
39
            error => 'Class must be instantiated by providing a Koha::Biblio object.'
40
        );
41
    }
42
    unless (ref($biblio) eq 'Koha::Biblio') {
43
        Koha::Exceptions::BadParameter->throw(
44
            error => 'Biblio must be a Koha::Biblio object.'
45
        );
46
    }
47
48
    my $self = {
49
        biblio => $biblio,
50
    };
51
52
    bless $self, $class;
53
}
54
55
=head3 checked_out
56
57
Returns Koha::Exceptions::Biblio::CheckedOut if biblio is checked out.
58
59
=cut
60
61
sub checked_out {
62
    my ($self, $patron) = @_;
63
64
    my $biblio = $self->biblio;
65
    my $issues = Koha::Checkouts->search({
66
        borrowernumber => 0+$patron->borrowernumber,
67
        biblionumber   => 0+$biblio->biblionumber,
68
    },
69
    {
70
        join => 'item',
71
    });
72
    if ($issues->count > 0) {
73
        return Koha::Exceptions::Biblio::CheckedOut->new(
74
            biblionumber => 0+$biblio->biblionumber,
75
        );
76
    }
77
    return;
78
}
79
80
=head3 forbid_holds_on_patrons_possessions
81
82
Returns Koha::Exceptions::Biblio::AnotherItemCheckedOut if system preference
83
AllowHoldsOnPatronsPossessions is disabled and another item from the same biblio
84
is checked out.
85
86
=cut
87
88
sub forbid_holds_on_patrons_possessions {
89
    my ($self, $patron) = @_;
90
91
    if (!C4::Context->preference('AllowHoldsOnPatronsPossessions')) {
92
        return $self->checked_out($patron);
93
    }
94
    return;
95
}
96
97
=head3 forbid_multiple_issues
98
99
Returns Koha::Exceptions::Biblio::CheckedOut if system preference
100
AllowMultipleIssuesOnABiblio is disabled and an item from biblio is checked out.
101
102
=cut
103
104
sub forbid_multiple_issues {
105
    my ($self, $patron) = @_;
106
107
    if (!C4::Context->preference('AllowMultipleIssuesOnABiblio')) {
108
        my $biblionumber = $self->biblio->biblionumber;
109
        unless (C4::Serials::CountSubscriptionFromBiblionumber($biblionumber)) {
110
            return $self->checked_out($patron);
111
        }
112
    }
113
    return;
114
}
115
116
1;
(-)a/Koha/Availability/Checks/Biblioitem.pm (+86 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::Biblioitem;
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::Availability::Checks);
23
24
use C4::Context;
25
use C4::Members;
26
27
use Koha::Biblioitems;
28
29
use Koha::Exceptions::Patron;
30
31
sub new {
32
    my ($class, $biblioitem) = @_;
33
34
    unless ($biblioitem) {
35
        Koha::Exceptions::MissingParameter->throw(
36
            error => 'Biblioitem related holdability checks require a biblioitem. Not given.'
37
        );
38
    }
39
    unless (ref($biblioitem) eq 'Koha::Biblioitem') {
40
        Koha::Exceptions::BadParameter->throw(
41
            error => 'Biblioitem must be a Koha::Biblioitem object.'
42
        );
43
    }
44
45
    my $self = {
46
        biblioitem => $biblioitem,
47
    };
48
49
    bless $self, $class;
50
}
51
52
=head3 age_restricted
53
54
Returns Koha::Exceptions::Patron::AgeRestricted if biblioitem age restriction
55
applies for patron.
56
57
=cut
58
59
sub age_restricted {
60
    my ($self, $patron) = @_;
61
62
    unless ($patron) {
63
        Koha::Exceptions::MissingParameter->throw(
64
            error => 'Patron related holdability checks require a patron. Not given.'
65
        );
66
    }
67
    unless (ref($patron) eq 'Koha::Patron') {
68
        Koha::Exceptions::BadParameter->throw(
69
            error => 'Patron must be a Koha::Patron object.'
70
        );
71
    }
72
73
    my $biblioitem = $self->biblioitem;
74
    my $agerestriction  = $biblioitem->agerestriction;
75
    my ($restriction_age, $daysToAgeRestriction) =
76
        C4::Circulation::GetAgeRestriction($agerestriction, $patron->unblessed);
77
    my $restricted = $daysToAgeRestriction && $daysToAgeRestriction > 0 ? 1:0;
78
    if ($restricted) {
79
        return Koha::Exceptions::Patron::AgeRestricted->new(
80
            age_restriction => $agerestriction,
81
        );
82
    }
83
    return;
84
}
85
86
1;
(-)a/Koha/Availability/Checks/Checkout.pm (+108 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::Checkout;
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::Availability::Checks);
23
24
use C4::Circulation;
25
use C4::Context;
26
27
use Koha::DateUtils;
28
use Koha::Items;
29
30
use Koha::Exceptions::Biblio;
31
use Koha::Exceptions::Checkout;
32
33
sub new {
34
    my ($class) = @_;
35
36
    my $self = {};
37
38
    bless $self, $class;
39
}
40
41
=head3 invalid_due_date
42
43
Returns Koha::Exceptions::Checkout::InvalidDueDate if given due date is invalid.
44
45
Returns Koha::Exceptions::Checkout::DueDateBeforeNow if given due date is in the
46
past.
47
48
=cut
49
50
sub invalid_due_date {
51
    my ($self, $item, $patron, $duedate) = @_;
52
53
    if ($duedate && ref $duedate ne 'DateTime') {
54
        eval { $duedate = dt_from_string($duedate); };
55
        if ($@) {
56
            return Koha::Exceptions::Checkout::InvalidDueDate->new(
57
                duedate => $duedate,
58
            );
59
        }
60
    }
61
62
    my $now = DateTime->now(time_zone => C4::Context->tz());
63
    unless ($duedate) {
64
        my $issuedate = $now->clone();
65
66
        my $branch = C4::Circulation::_GetCircControlBranch($item, $patron);
67
        $duedate = C4::Circulation::CalcDateDue
68
        (
69
            $issuedate, $item->effective_itemtype, $branch, $patron->unblessed
70
        );
71
    }
72
    if ($duedate) {
73
        my $today = $now->clone->truncate(to => 'minute');
74
        if (DateTime->compare($duedate,$today) == -1 ) {
75
            # duedate cannot be before now
76
            return Koha::Exceptions::Checkout::DueDateBeforeNow->new(
77
                duedate => $duedate->strftime('%F %T'),
78
                now => $now->strftime('%F %T'),
79
            );
80
        }
81
    } else {
82
        return Koha::Exceptions::Checkout::InvalidDueDate->new(
83
                duedate => $duedate,
84
        );
85
    }
86
    return;
87
}
88
89
=head3 no_more_renewals
90
91
Returns Koha::Exceptions::Checkout::NoMoreRenewals if no more renewals are
92
allowed for given checkout.
93
94
=cut
95
96
sub no_more_renewals {
97
    my ($self, $issue) = @_;
98
99
    return unless $issue;
100
    my ($status) = C4::Circulation::CanBookBeRenewed($issue->borrowernumber,
101
             $issue->itemnumber);
102
    if ($status == 0) {
103
        return Koha::Exceptions::Checkout::NoMoreRenewals->new;
104
    }
105
    return;
106
}
107
108
1;
(-)a/Koha/Availability/Checks/IssuingRule.pm (+413 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::IssuingRule;
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::Availability::Checks);
23
24
use C4::Circulation;
25
use C4::Context;
26
use C4::Reserves;
27
28
use Koha::IssuingRules;
29
use Koha::Items;
30
31
use Koha::Exceptions;
32
use Koha::Exceptions::Checkout;
33
use Koha::Exceptions::Hold;
34
35
=head3 new
36
37
OPTIONAL PARAMETERS:
38
39
* categorycode      Attempts to match issuing rule with given categorycode
40
* rule_itemtype     Attempts to match issuing rule with given itemtype
41
* branchcode        Attempts to match issuing rule with given branchcode
42
43
* patron            Stores patron into the object for reusability. Also
44
                    attempts to match issuing rule with item's itemtype
45
                    unless specificed with "rule_itemtype" parameter.
46
* item              stores item into the object for reusability. Also
47
                    Attempts to match issuing rule with patron's categorycode
48
                    unless specified with "categorycode" parameter.
49
* biblioitem        Attempts to match issuing rule with itemtype from given
50
                    biblioitem as a fallback
51
52
Stores the effective issuing rule into class variable effective_issuing_rule
53
for reusability. This means the methods in this class are performed on the
54
stored effective issuing rule and multiple queries into issuingrules table
55
are avoided.
56
57
Caches issuing rule momentarily to help performance in biblio availability
58
calculation. This is helpful because a biblio may have multiple items matching
59
the same issuing rule and this lets us avoid multiple, unneccessary queries into
60
the issuingrule table.
61
62
=cut
63
64
sub new {
65
    my $class = shift;
66
    my ($params) = @_;
67
68
    my $self = $class->SUPER::new(@_);
69
70
    my $categorycode  = $params->{'categorycode'};
71
    my $rule_itemtype = $params->{'rule_itemtype'};
72
    my $branchcode    = $params->{'branchcode'};
73
74
    my $patron     = $self->_validate_parameter($params,
75
                        'patron',     'Koha::Patron');
76
    my $item       = $self->_validate_parameter($params,
77
                        'item',       'Koha::Item');
78
    my $biblioitem = $self->_validate_parameter($params,
79
                        'biblioitem', 'Koha::Biblioitem');
80
81
    unless ($rule_itemtype) {
82
        $rule_itemtype = $item
83
            ? $item->effective_itemtype
84
            : $biblioitem
85
              ? $biblioitem->itemtype
86
              : undef;
87
    }
88
    unless ($categorycode) {
89
        $categorycode = $patron ? $patron->categorycode : undef;
90
    }
91
    if ($params->{'use_cache'}) {
92
        $self->{'use_cache'} = 1;
93
    } else {
94
        $self->{'use_cache'} = 0;
95
    }
96
97
    # Get a matching issuing rule
98
    my $rule;
99
    my $cache;
100
    if ($self->use_cache) {
101
        $cache = Koha::Caches->get_instance('availability');
102
        my $cached = $cache->get_from_cache('issuingrule-.'
103
                                .($categorycode?$categorycode:'*').'-'
104
                                .($rule_itemtype?$rule_itemtype:'*').'-'
105
                                .($branchcode?$branchcode:'*'));
106
        if ($cached) {
107
            $rule = Koha::IssuingRule->new->set($cached);
108
        }
109
    }
110
111
    unless ($rule) {
112
        $rule = Koha::IssuingRules->get_effective_issuing_rule({
113
            categorycode => $categorycode,
114
            itemtype     => $rule_itemtype,
115
            branchcode   => $branchcode,
116
        });
117
        if ($rule && $self->use_cache) {
118
            $cache->set_in_cache('issuingrule-.'
119
                    .($categorycode?$categorycode:'*').'-'
120
                    .($rule_itemtype?$rule_itemtype:'*').'-'
121
                    .($branchcode?$branchcode:'*'),
122
                    $rule->unblessed, { expiry => 10 });
123
        }
124
    }
125
126
    # Store effective issuing rule into object
127
    $self->{'effective_issuing_rule'} = $rule;
128
    # Store patron into object
129
    $self->{'patron'} = $patron;
130
    # Store item into object
131
    $self->{'item'} = $item;
132
133
    bless $self, $class;
134
}
135
136
=head3 maximum_checkouts_reached
137
138
Returns Koha::Exceptions::Checkout::MaximumCheckoutsReached if maximum number
139
of checkouts have been reached by patron.
140
141
=cut
142
143
sub maximum_checkouts_reached {
144
    my ($self, $item, $patron) = @_;
145
146
    return unless $patron ||= $self->patron;
147
    return unless $item ||= $self->item;
148
149
    my $item_unblessed = $item->unblessed;
150
    $item_unblessed->{'itemtype'} = $item->effective_itemtype;
151
    my $toomany = C4::Circulation::TooMany(
152
            $patron->unblessed,
153
            $item->biblionumber,
154
            $item_unblessed,
155
    );
156
157
    if ($toomany) {
158
        return Koha::Exceptions::Checkout::MaximumCheckoutsReached->new(
159
            error => $toomany->{reason},
160
            max_checkouts_allowed => 0+$toomany->{max_allowed},
161
            current_checkout_count => 0+$toomany->{count},
162
        );
163
    }
164
    return;
165
}
166
167
=head3 maximum_holds_for_record_reached
168
169
Returns Koha::Exceptions::Hold::MaximumHoldsForRecordReached if maximum number
170
of holds on biblio have been reached by patron.
171
172
Returns Koha::Exceptions::Hold::ZeroHoldsAllowed if no holds are allowed at all.
173
174
OPTIONAL PARAMETERS:
175
nonfound_holds      Allows you to pass Koha::Holds object to improve performance
176
                    by avoiding another query to reserves table.
177
                    (Found holds don't count against a patron's holds limit)
178
biblionumber        Allows you to specify biblionumber; if not given, item's
179
                    biblionumber will be used (recommended; but requires you to
180
                    provide item while instantiating this class).
181
182
=cut
183
184
sub maximum_holds_for_record_reached {
185
    my ($self, $params) = @_;
186
187
    return unless my $rule = $self->effective_issuing_rule;
188
    return unless my $item = $self->item;
189
190
    my $biblionumber = $params->{'biblionumber'} ? $params->{'biblionumber'}
191
                : $item->biblionumber;
192
    if ($rule->holds_per_record > 0 && $rule->reservesallowed > 0) {
193
        my $holds_on_this_record;
194
        unless (exists $params->{'nonfound_holds'}) {
195
            $holds_on_this_record = Koha::Holds->search({
196
                borrowernumber => 0+$self->patron->borrowernumber,
197
                biblionumber   => 0+$biblionumber,
198
                found          => undef,
199
            })->count;
200
        } else {
201
            $holds_on_this_record = @{$params->{'nonfound_holds'}};
202
        }
203
        if ($holds_on_this_record >= $rule->holds_per_record) {
204
            return Koha::Exceptions::Hold::MaximumHoldsForRecordReached->new(
205
                max_holds_allowed => 0+$rule->holds_per_record,
206
                current_hold_count => 0+$holds_on_this_record,
207
            );
208
        }
209
    } else {
210
        return $self->zero_holds;
211
    }
212
    return;
213
}
214
215
=head3 maximum_holds_reached
216
217
Returns Koha::Exceptions::Hold::MaximumHoldsReached if maximum number
218
of holds have been reached by patron.
219
220
Returns Koha::Exceptions::Hold::ZeroHoldsAllowed if no holds are allowed at all.
221
222
=cut
223
224
sub maximum_holds_reached {
225
    my ($self) = @_;
226
227
    return unless my $rule = $self->effective_issuing_rule;
228
    my $rule_itemtype = $rule->itemtype;
229
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
230
    if ($rule->holds_per_record > 0 && $rule->reservesallowed > 0) {
231
        # Get patron's hold count for holds that match the found issuing rule
232
        my $hold_count = $self->_patron_hold_count($rule_itemtype, $controlbranch);
233
        if ($hold_count >= $rule->reservesallowed) {
234
            return Koha::Exceptions::Hold::MaximumHoldsReached->new(
235
                max_holds_allowed => 0+$rule->reservesallowed,
236
                current_hold_count => 0+$hold_count,
237
            );
238
        }
239
    } else {
240
        return $self->zero_holds;
241
    }
242
    return;
243
}
244
245
=head3 on_shelf_holds_forbidden
246
247
Returns Koha::Exceptions::Hold::OnShelfNotAllowed if effective issuing rule
248
restricts on-shelf holds.
249
250
=cut
251
252
sub on_shelf_holds_forbidden {
253
    my ($self) = @_;
254
255
    return unless my $rule = $self->effective_issuing_rule;
256
    return unless my $item = $self->item;
257
    my $on_shelf_holds = $rule->onshelfholds;
258
259
    if ($on_shelf_holds == 0) {
260
        my $hold_waiting = Koha::Holds->search({
261
            found => 'W',
262
            itemnumber => 0+$item->itemnumber,
263
            priority => 0
264
        })->count;
265
        if (!$item->onloan && !$hold_waiting) {
266
            return Koha::Exceptions::Hold::OnShelfNotAllowed->new;
267
        }
268
        return;
269
    } elsif ($on_shelf_holds == 1) {
270
        return;
271
    } elsif ($on_shelf_holds == 2) {
272
        my @items = Koha::Items->search({ biblionumber => $item->biblionumber });
273
274
        my $any_available = 0;
275
276
        foreach my $i (@items) {
277
            unless ($i->itemlost
278
              || $i->notforloan > 0
279
              || $i->withdrawn
280
              || $i->onloan
281
              || C4::Reserves::IsItemOnHoldAndFound( $i->id )
282
              || ( $i->damaged
283
                && !C4::Context->preference('AllowHoldsOnDamagedItems') )
284
              || Koha::ItemTypes->find( $i->effective_itemtype() )->notforloan) {
285
                $any_available = 1;
286
                last;
287
            }
288
        }
289
        return Koha::Exceptions::Hold::OnShelfNotAllowed->new if $any_available;
290
    }
291
    return;
292
}
293
294
=head3 opac_item_level_hold_forbidden
295
296
Returns Koha::Exceptions::Hold::ItemLevelHoldNotAllowed if item-level holds are
297
forbidden in OPAC.
298
299
=cut
300
301
sub opac_item_level_hold_forbidden {
302
    my ($self) = @_;
303
304
    return unless my $rule = $self->effective_issuing_rule;
305
    if (defined $rule->opacitemholds && $rule->opacitemholds eq 'N') {
306
        return Koha::Exceptions::Hold::ItemLevelHoldNotAllowed->new;
307
    }
308
    return;
309
}
310
311
=head3 zero_checkouts_allowed
312
313
Returns Koha::Exceptions::Checkout::ZeroCheckoutsAllowed if checkouts are not
314
allowed at all.
315
316
=cut
317
318
sub zero_checkouts_allowed {
319
    my ($self) = @_;
320
321
    return unless my $rule = $self->effective_issuing_rule;
322
    if (defined $rule->maxissueqty && $rule->maxissueqty == 0) {
323
        return Koha::Exceptions::Checkout::ZeroCheckoutsAllowed->new;
324
    }
325
    return;
326
}
327
328
=head3 zero_holds_allowed
329
330
Returns Koha::Exceptions::Hold::ZeroHoldsAllowed if holds are not
331
allowed at all.
332
333
This will inspect both "reservesallowed" and "holds_per_record" value in effective
334
issuing rule.
335
336
=cut
337
338
sub zero_holds_allowed {
339
    my ($self) = @_;
340
341
    return unless my $rule = $self->effective_issuing_rule;
342
    if (defined $rule->reservesallowed && $rule->reservesallowed == 0
343
        || defined $rule->holds_per_record && $rule->holds_per_record == 0) {
344
        return Koha::Exceptions::Hold::ZeroHoldsAllowed->new;
345
    }
346
    return;
347
}
348
349
sub _patron_hold_count {
350
    my ($self, $itemtype, $controlbranch) = @_;
351
352
    $itemtype ||= '*';
353
    my $branchcode;
354
    my $branchfield = 'me.branchcode';
355
    $controlbranch ||= C4::Context->preference('ReservesControlBranch');
356
    my $patron = $self->patron;
357
    if ($self->patron && $controlbranch eq 'PatronLibrary') {
358
        $branchfield = 'borrower.branchcode';
359
        $branchcode = $patron->branchcode;
360
    } elsif ($self->item && $controlbranch eq 'ItemHomeLibrary') {
361
        $branchfield = 'item.homebranch';
362
        $branchcode = $self->item->homebranch;
363
    }
364
365
    my $cache;
366
    if ($self->use_cache) {
367
        $cache = Koha::Caches->get_instance('availability');
368
        my $cached = $cache->get_from_cache('holds_of_'.$patron->borrowernumber.'-'
369
                                            .$itemtype.'-'.$branchcode);
370
        if (defined $cached) {
371
            return $cached;
372
        }
373
    }
374
375
    my $holds = Koha::Holds->search({
376
        'me.borrowernumber' => $patron->borrowernumber,
377
        $branchfield => $branchcode,
378
        '-and' => [
379
            '-or' => [
380
                $itemtype ne '*' && C4::Context->preference('item-level_itypes') == 1 ? [
381
                    { 'item.itype' => $itemtype },
382
                        { 'biblioitem.itemtype' => $itemtype }
383
                ] : [ { 'biblioitem.itemtype' => $itemtype } ]
384
            ]
385
        ]}, {
386
        join => ['borrower', 'biblioitem', 'item'],
387
        '+select' => [ 'borrower.branchcode', 'item.homebranch' ],
388
        '+as' => ['borrower.branchcode', 'item.homebranch' ]
389
    })->count;
390
391
    if ($self->use_cache) {
392
        $cache->set_in_cache('holds_of_'.$patron->borrowernumber.'-'
393
                    .$itemtype.'-'.$branchcode, $holds, { expiry => 10 });
394
    }
395
396
    return $holds;
397
}
398
399
sub _validate_parameter {
400
    my ($self, $params, $key, $ref) = @_;
401
402
    if (exists $params->{$key}) {
403
        if (ref($params->{$key}) eq $ref) {
404
            return $params->{$key};
405
        } else {
406
            Koha::Exceptions::BadParameter->throw(
407
                "Parameter $key must be a $ref object."
408
            );
409
        }
410
    }
411
}
412
413
1;
(-)a/Koha/Availability/Checks/Item.pm (+458 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::Item;
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::Availability::Checks);
23
24
use C4::Circulation;
25
use C4::Context;
26
use C4::Reserves;
27
28
use Koha::AuthorisedValues;
29
use Koha::DateUtils;
30
use Koha::Holds;
31
use Koha::ItemTypes;
32
use Koha::Items;
33
use Koha::Item::Transfers;
34
35
use Koha::Exceptions::Item;
36
use Koha::Exceptions::ItemType;
37
38
sub new {
39
    my ($class, $item) = @_;
40
41
    unless ($item) {
42
        Koha::Exceptions::MissingParameter->throw(
43
            error => 'Class must be instantiated by providing a Koha::Item object.'
44
        );
45
    }
46
    unless (ref($item) eq 'Koha::Item') {
47
        Koha::Exceptions::BadParameter->throw(
48
            error => 'Item must be a Koha::Item object.'
49
        );
50
    }
51
52
    my $self = {
53
        item => $item,
54
    };
55
56
    bless $self, $class;
57
}
58
59
=head3 checked_out
60
61
Returns Koha::Exceptions::Item::CheckedOut if item is checked out.
62
63
=cut
64
65
sub checked_out {
66
    my ($self, $issue) = @_;
67
68
    $issue ||= C4::Circulation::GetItemIssue($self->item->itemnumber);
69
    if (ref($issue) eq 'Koha::Checkout') {
70
        $issue = $issue->unblessed;
71
        $issue->{date_due} = dt_from_string($issue->{date_due});
72
    }
73
    if ($issue) {
74
        return Koha::Exceptions::Item::CheckedOut->new(
75
            borrowernumber => 0+$issue->{borrowernumber},
76
            date_due => $issue->{date_due}->strftime('%F %T'),
77
        );
78
    }
79
    return;
80
}
81
82
=head3 checked_out
83
84
Returns Koha::Exceptions::Checkout::Fee if checking out an item will cause
85
a checkout fee.
86
87
Koha::Exceptions::Checkout::Fee additional fields:
88
  amount                # defines the amount of checkout fee
89
90
=cut
91
92
sub checkout_fee {
93
    my ($self, $patron) = @_;
94
95
    my ($rentalCharge) = C4::Circulation::GetIssuingCharges
96
    (
97
        $self->item->itemnumber,
98
        $patron ? $patron->borrowernumber : undef
99
    );
100
    if ($rentalCharge > 0){
101
        return Koha::Exceptions::Checkout::Fee->new(
102
            amount => sprintf("%.02f", $rentalCharge),
103
        );
104
    }
105
    return;
106
}
107
108
=head3 damaged
109
110
Returns Koha::Exceptions::Item::Damaged if item is damaged and holds are not
111
allowed on damaged items.
112
113
=cut
114
115
sub damaged {
116
    my ($self) = @_;
117
118
    if ($self->item->damaged
119
        && !C4::Context->preference('AllowHoldsOnDamagedItems')) {
120
        return Koha::Exceptions::Item::Damaged->new;
121
    }
122
    return;
123
}
124
125
=head3 from_another_library
126
127
Returns Koha::Exceptions::Item::FromAnotherLibrary if IndependentBranches is on,
128
and item is from another branch than the user currently logged in.
129
130
Koha::Exceptions::Item::FromAnotherLibrary additional fields:
131
  from_library              # item's library (according to HomeOrHoldingBranch)
132
  current_library           # the library of logged-in user
133
134
=cut
135
136
sub from_another_library {
137
    my ($self) = @_;
138
139
    my $item = $self->item;
140
    if (C4::Context->preference("IndependentBranches")) {
141
        return unless my $userenv = C4::Context->userenv;
142
        unless (C4::Context->IsSuperLibrarian()) {
143
            my $homeorholding = C4::Context->preference("HomeOrHoldingBranch");
144
            if ($userenv->{branch} && $item->$homeorholding ne $userenv->{branch}){
145
                return Koha::Exceptions::Item::FromAnotherLibrary->new(
146
                        from_library => $item->$homeorholding,
147
                        current_library => $userenv->{branch},
148
                );
149
            }
150
        }
151
    }
152
    return;
153
}
154
155
=head3 held
156
157
Returns Koha::Exceptions::Item::Held item is held.
158
159
Koha::Exceptions::Item::Held additional fields:
160
  borrowernumber              # item's library (according to HomeOrHoldingBranch)
161
  status                      # the library of logged-in user
162
  hold_queue_length           # hold queue length for the item
163
164
=cut
165
166
sub held {
167
    my ($self) = @_;
168
169
    my $item = $self->item;
170
    if (my ($s, $reserve) = C4::Reserves::CheckReserves($item->itemnumber)) {
171
        if ($reserve) {
172
            return Koha::Exceptions::Item::Held->new(
173
                borrowernumber => 0+$reserve->{'borrowernumber'},
174
                status => $s,
175
                hold_queue_length => 0+Koha::Holds->search({
176
                    itemnumber => $item->itemnumber })->count,
177
            );
178
        }
179
    }
180
    return;
181
}
182
183
=head3 held_by_patron
184
185
Returns Koha::Exceptions::Item::AlreadyHeldForThisPatron if item is already
186
held by given patron.
187
188
OPTIONAL PARAMETERS
189
holds       # list of Koha::Hold objects to inspect the item's held-status from.
190
            # If not given, a query is made for selecting the holds from database.
191
            # Useful in optimizing biblio-level availability by selecting all holds
192
            # of a biblio and then passing it for this function instead of querying
193
            # reserves table multiple times for each item.
194
195
=cut
196
197
sub held_by_patron {
198
    my ($self, $patron, $params) = @_;
199
200
    my $item = $self->item;
201
    my $holds;
202
    if (!exists $params->{'holds'}) {
203
        $holds = Koha::Holds->search({
204
            borrowernumber => 0+$patron->borrowernumber,
205
            itemnumber => 0+$item->itemnumber,
206
        })->count();
207
    } else {
208
        foreach my $hold (@{$params->{'holds'}}) {
209
            next unless $hold->itemnumber;
210
            if ($hold->itemnumber == $item->itemnumber) {
211
                $holds++;
212
            }
213
        }
214
    }
215
    if ($holds) {
216
        return Koha::Exceptions::Item::AlreadyHeldForThisPatron->new
217
    }
218
    return;
219
}
220
221
=head3 high_hold
222
223
Returns Koha::Exceptions::Item::HighHolds if item is a high-held item and
224
decreaseLoanHighHolds is enabled.
225
226
=cut
227
228
sub high_hold {
229
    my ($self, $patron) = @_;
230
231
    return unless C4::Context->preference('decreaseLoanHighHolds');
232
233
    my $item = $self->item;
234
    my $check = C4::Circulation::checkHighHolds(
235
        $item->unblessed,
236
        $patron->unblessed
237
    );
238
239
    if ($check->{exceeded}) {
240
        return Koha::Exceptions::Item::HighHolds->new(
241
            num_holds => 0+$check->{outstanding},
242
            duration => $check->{duration},
243
            returndate => $check->{due_date}->strftime('%F %T'),
244
        );
245
    }
246
    return;
247
}
248
249
=head3 lost
250
251
Returns Koha::Exceptions::Item::Lost if item is lost.
252
253
=cut
254
255
sub lost {
256
    my ($self) = @_;
257
258
    my $item = $self->item;
259
    if ($self->item->itemlost) {
260
        my $av = Koha::AuthorisedValues->search({
261
            category => 'LOST',
262
            authorised_value => $item->itemlost
263
        });
264
        my $code = $av->count ? $av->next->lib : '';
265
        return Koha::Exceptions::Item::Lost->new(
266
            status => 0+$item->itemlost,
267
            code => $code,
268
        );
269
    }
270
    return;
271
}
272
273
=head3 notforloan
274
275
Returns Koha::Exceptions::Item::NotForLoan if item is not for loan, and
276
additionally Koha::Exceptions::ItemType::NotForLoan if itemtype is not for loan.
277
278
=cut
279
280
sub notforloan {
281
    my ($self) = @_;
282
283
    my $item = $self->item;
284
    my $cache = Koha::Caches->get_instance('availability');
285
    my $cached = $cache->get_from_cache('itemtype-'.$item->effective_itemtype);
286
    my $itemtype;
287
    if ($cached) {
288
        $itemtype = Koha::ItemType->new->set($cached);
289
    } else {
290
        $itemtype = Koha::ItemTypes->find($item->effective_itemtype);
291
        $cache->set_in_cache('itemtype-'.$item->effective_itemtype,
292
                            $itemtype->unblessed, { expiry => 10 }) if $itemtype;
293
    }
294
295
    if ($item->notforloan != 0 || $itemtype && $itemtype->notforloan != 0) {
296
        my $av = Koha::AuthorisedValues->search({
297
            category => 'NOT_LOAN',
298
            authorised_value => $item->notforloan
299
        });
300
        my $code = $av->count ? $av->next->lib : '';
301
        if ($item->notforloan > 0) {
302
            return Koha::Exceptions::Item::NotForLoan->new(
303
                status => 0+$item->notforloan,
304
                code => $code,
305
            );
306
        } elsif ($itemtype && $itemtype->notforloan > 0) {
307
            return Koha::Exceptions::ItemType::NotForLoan->new(
308
                status => 0+$itemtype->notforloan,
309
                code => $code,
310
                itemtype => $itemtype->itemtype,
311
            );
312
        } elsif ($item->notforloan < 0) {
313
            return Koha::Exceptions::Item::NotForLoan->new(
314
                status => 0+$item->notforloan,
315
                code => $code,
316
            );
317
        }
318
    }
319
    return;
320
}
321
322
=head3 onloan
323
324
Returns Koha::Exceptions::Item::CheckedOut if item is onloan.
325
326
This does not query issues table, but simply checks item's onloan-column.
327
328
=cut
329
330
sub onloan {
331
    my ($self) = @_;
332
333
    # This simply checks item's onloan-column to determine item's checked out
334
    # -status. Use C<checked_out> to perform an actual query for checkouts.
335
    if ($self->item->onloan) {
336
        return Koha::Exceptions::Item::CheckedOut->new;
337
    }
338
}
339
340
=head3 restricted
341
342
Returns Koha::Exceptions::Item::Restricted if item is restricted.
343
344
=cut
345
346
sub restricted {
347
    my ($self) = @_;
348
349
    if ($self->item->restricted) {
350
        return Koha::Exceptions::Item::Restricted->new;
351
    }
352
    return;
353
}
354
355
=head3 transfer
356
357
Returns Koha::Exceptions::Item::Transfer if item is in transfer.
358
359
Koha::Exceptions::Item::Transfer additional fields:
360
  from_library
361
  to_library
362
  datesent
363
364
=cut
365
366
sub transfer {
367
    my ($self) = @_;
368
369
    my $transfer = Koha::Item::Transfers->search({
370
        itemnumber => $self->item->itemnumber,
371
        datesent => { '!=', undef },
372
        datearrived => undef,
373
    })->next;
374
    if ($transfer) {
375
        return Koha::Exceptions::Item::Transfer->new(
376
            from_library => $transfer->frombranch,
377
            to_library => $transfer->tobranch,
378
            datesent => $transfer->datesent,
379
        );
380
    }
381
    return;
382
}
383
384
=head3 transfer_limit
385
386
Returns Koha::Exceptions::Item::CannotBeTransferred a transfer limit applies
387
for item.
388
389
Koha::Exceptions::Item::CannotBeTransferred additional parameters:
390
  from_library
391
  to_library
392
393
=cut
394
395
sub transfer_limit {
396
    my ($self, $to_branch) = @_;
397
398
    return unless C4::Context->preference('UseBranchTransferLimits');
399
    my $item = $self->item;
400
    my $limit_type = C4::Context->preference('BranchTransferLimitsType');
401
    my $code;
402
    if ($limit_type eq 'itemtype') {
403
        $code = $item->effective_itemtype;
404
    } elsif ($limit_type eq 'ccode') {
405
        $code = $item->ccode;
406
    } else {
407
        Koha::Exceptions::BadParameter->throw(
408
            error => 'System preference BranchTransferLimitsType has an'
409
            .' unrecognized value.'
410
        );
411
    }
412
413
    my $allowed = C4::Circulation::IsBranchTransferAllowed(
414
        $to_branch,
415
        $item->holdingbranch,
416
        $code
417
    );
418
    if (!$allowed) {
419
        return Koha::Exceptions::Item::CannotBeTransferred->new(
420
            from_library => $item->holdingbranch,
421
            to_library   => $to_branch,
422
        );
423
    }
424
    return;
425
}
426
427
=head3 unknown_barcode
428
429
Returns Koha::Exceptions::Item::UnknownBarcode if item has no barcode.
430
431
=cut
432
433
sub unknown_barcode {
434
    my ($self) = @_;
435
436
    my $item = $self->item;
437
    unless ($item->barcode) {
438
        return Koha::Exceptions::Item::UnknownBarcode->new;
439
    }
440
    return;
441
}
442
443
=head3 withdrawn
444
445
Returns Koha::Exceptions::Item::Withdrawn if item is withdrawn.
446
447
=cut
448
449
sub withdrawn {
450
    my ($self) = @_;
451
452
    if ($self->item->withdrawn) {
453
        return Koha::Exceptions::Item::Withdrawn->new;
454
    }
455
    return;
456
}
457
458
1;
(-)a/Koha/Availability/Checks/LibraryItemRule.pm (+144 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::LibraryItemRule;
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::Availability::Checks);
23
24
use C4::Circulation;
25
26
use Koha::Exceptions::Hold;
27
28
=head3 new
29
30
With given parameters item (Koha::Item) and patron (Koha::Patron), selects the
31
effective library item rule and stores it into this object. Further calls to
32
methods of this object will use this library item rule.
33
34
PARAMETERS:
35
item    # Koha::Item object
36
patron  # Koha::Patron object
37
38
=cut
39
40
sub new {
41
    my $class = shift;
42
    my ($params) = @_;
43
44
    my $self = $class->SUPER::new(@_);
45
46
    my $independentBranch = C4::Context->preference('IndependentBranches');
47
    my $circcontrol       = C4::Context->preference('CircControl');
48
    my $patron = $self->{'patron'} = $self->_validate_parameter($params,
49
                                            'patron', 'Koha::Patron');
50
    my $item   = $self->{'item'} = $self->_validate_parameter($params,
51
                                            'item',   'Koha::Item');
52
53
    my $circ_control_branch;
54
    my $branchitemrule;
55
    if ($circcontrol eq 'ItemHomeLibrary' && $item) {
56
        $circ_control_branch = C4::Circulation::_GetCircControlBranch(
57
                                $item->unblessed, undef);
58
    } elsif ($circcontrol eq 'PatronLibrary' && $patron) {
59
        $circ_control_branch = C4::Circulation::_GetCircControlBranch(
60
                                undef, $patron->unblessed);
61
    } elsif ($circcontrol eq 'PickupLibrary') {
62
        $circ_control_branch = C4::Circulation::_GetCircControlBranch(
63
                                undef, undef);
64
    } else {
65
        bless $self, $class;
66
        return $self;
67
    }
68
69
    $self->{'branchitemrule'} = C4::Circulation::GetBranchItemRule(
70
                        $circ_control_branch, $item->effective_itemtype);
71
72
    bless $self, $class;
73
}
74
75
=head3 hold_not_allowed_by_library
76
77
Returns Koha::Exceptions::Hold::NotAllowedByLibrary if library item rules does
78
not allow holds.
79
80
=cut
81
82
sub hold_not_allowed_by_library {
83
    my ($self) = @_;
84
85
    return unless my $rule = $self->branchitemrule;
86
87
    if (!$rule->{holdallowed}) {
88
        return Koha::Exceptions::Hold::NotAllowedByLibrary->new
89
    }
90
    return;
91
}
92
93
=head3 hold_not_allowed_by_library
94
95
Returns Koha::Exceptions::Hold::NotAllowedFromOtherLibraries if library item rules
96
define restrictions for holds on items that are from another library than patron.
97
98
=cut
99
100
sub hold_not_allowed_by_other_library {
101
    my ($self) = @_;
102
103
    return unless my $rule = $self->branchitemrule;
104
    return unless my $item = $self->item;
105
106
    my $fromotherbranches = C4::Context->preference('canreservefromotherbranches');
107
    my $independentBranch = C4::Context->preference('IndependentBranches');
108
109
    my $patron = $self->patron;
110
    if ($rule->{holdallowed} == 1) {
111
        if (!$patron) {
112
            # Since we don't know who is asking for item and from which
113
            # library, return NotAllowedFromOtherLibraries, but it should
114
            # be set only as an additional availability note
115
            return Koha::Exceptions::Hold::NotAllowedFromOtherLibraries->new;
116
        } elsif ($patron && $patron->branchcode ne $item->homebranch) {
117
            return Koha::Exceptions::Hold::NotAllowedFromOtherLibraries->new;
118
        }
119
    } elsif ($independentBranch && !$fromotherbranches) {
120
        if (!$patron) {
121
            # This should be stored as an additional note
122
            return Koha::Exceptions::Hold::NotAllowedFromOtherLibraries->new
123
        } elsif ($patron && $item->homebranch ne $patron->branchcode) {
124
            return Koha::Exceptions::Hold::NotAllowedFromOtherLibraries->new
125
        }
126
    }
127
    return;
128
}
129
130
sub _validate_parameter {
131
    my ($self, $params, $key, $ref) = @_;
132
133
    if (exists $params->{$key}) {
134
        if (ref($params->{$key}) eq $ref) {
135
            return $params->{$key};
136
        } else {
137
            Koha::Exceptions::BadParameter->throw(
138
                "Parameter $key must be a $ref object."
139
            );
140
        }
141
    }
142
}
143
144
1;
(-)a/Koha/Availability/Checks/Patron.pm (+326 lines)
Line 0 Link Here
1
package Koha::Availability::Checks::Patron;
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::Availability::Checks);
23
24
use Scalar::Util qw(looks_like_number);
25
26
use C4::Context;
27
use C4::Members;
28
29
use Koha::Exceptions::Hold;
30
use Koha::Exceptions::Patron;
31
32
sub new {
33
    my ($class, $patron) = @_;
34
35
    unless ($patron) {
36
        Koha::Exceptions::MissingParameter->throw(
37
            error => 'Patron related holdability checks require a patron. Not given.'
38
        );
39
    }
40
    unless (ref($patron) eq 'Koha::Patron') {
41
        Koha::Exceptions::BadParameter->throw(
42
            error => 'Patron must be a Koha::Patron object.'
43
        );
44
    }
45
46
    my $self = {
47
        patron => $patron,
48
    };
49
50
    bless $self, $class;
51
}
52
53
=head3 debarred
54
55
Returns Koha::Exceptions::Patron::Debarred if patron is debarred.
56
57
Koha::Exceptions::Patron::Debarred additional fields:
58
  expiration     # expiration date of debarment
59
  comment        # comment for this debarment
60
61
=cut
62
63
sub debarred {
64
    my ($self) = @_;
65
66
    my $patron = $self->patron;
67
    if ($patron->is_debarred) {
68
        return Koha::Exceptions::Patron::Debarred->new(
69
            expiration => $patron->debarred,
70
            comment => $patron->debarredcomment,
71
        );
72
    }
73
    return;
74
}
75
76
=head3 debt_checkout
77
78
Returns Koha::Exceptions::Patron::Debt if patron has outstanding fines that
79
exceed the amount defined in "noissuescharge" system preference.
80
81
Koha::Exceptions::Patron::Debt additional fields:
82
  max_outstanding       # maximum allowed amount of outstanding fines
83
  current_outstanding   # current amount of outstanding fines
84
85
=cut
86
87
sub debt_checkout {
88
    my ($self) = @_;
89
90
    my ($amount) = C4::Members::GetMemberAccountRecords(
91
                        $self->patron->borrowernumber
92
    );
93
    my $maxoutstanding = C4::Context->preference("noissuescharge");
94
    if (C4::Context->preference('AllFinesNeedOverride') && $amount > 0) {
95
        # All fines need override, so return Koha::Exceptions::Patron::Debt.
96
        return Koha::Exceptions::Patron::Debt->new(
97
            max_outstanding => 0+sprintf("%.2f", $maxoutstanding),
98
            current_outstanding => 0+sprintf("%.2f", $amount),
99
        );
100
    } else {
101
        return $self->_debt($amount, $maxoutstanding);
102
    }
103
}
104
105
=head3 debt_checkout_guarantees
106
107
Returns Koha::Exceptions::Patron::DebtGuarantees if patron's guarantees are
108
exceeding the amount defined in "NoIssuesChargeGuarantees" system preference.
109
110
Koha::Exceptions::Patron::DebtGuarantees additional fields:
111
  max_outstanding       # maximum allowed amount of outstanding fines
112
  current_outstanding   # current amount of outstanding fines
113
114
=cut
115
116
sub debt_checkout_guarantees {
117
    my ($self) = @_;
118
119
    my $patron = $self->patron;
120
    my $max_charges = C4::Context->preference("NoIssuesChargeGuarantees");
121
    $max_charges = undef unless looks_like_number($max_charges);
122
    return unless $max_charges;
123
    my @guarantees = $patron->guarantees;
124
    return unless scalar(@guarantees);
125
    my $guarantees_non_issues_charges;
126
    foreach my $g (@guarantees) {
127
        my ($b, $n, $o) = C4::Members::GetMemberAccountBalance($g->id);
128
        $guarantees_non_issues_charges += $n;
129
    }
130
    if ($guarantees_non_issues_charges > $max_charges) {
131
        return Koha::Exceptions::Patron::DebtGuarantees->new(
132
            max_outstanding => 0+sprintf("%.2f", $max_charges),
133
            current_outstanding => 0+sprintf("%.2f", $guarantees_non_issues_charges),
134
        );
135
    }
136
    return;
137
}
138
139
=head3 debt_hold
140
141
Returns Koha::Exceptions::Patron::Debt if patron has outstanding fines that
142
exceed the amount defined in "maxoutstanding" system preference.
143
144
Koha::Exceptions::Patron::Debt additional fields:
145
  max_outstanding       # maximum allowed amount of outstanding fines
146
  current_outstanding   # current amount of outstanding fines
147
148
=cut
149
150
sub debt_hold {
151
    my ($self) = @_;
152
153
    my ($amount) = C4::Members::GetMemberAccountRecords(
154
                        $self->patron->borrowernumber
155
    );
156
    my $maxoutstanding = C4::Context->preference("maxoutstanding");
157
    return $self->_debt($amount, $maxoutstanding);
158
}
159
160
=head3 debt_checkout
161
162
Returns Koha::Exceptions::Patron::Debt if patron has outstanding fines that
163
exceed the amount defined in "OPACFineNoRenewals" system preference.
164
165
Koha::Exceptions::Patron::Debt additional fields:
166
  max_outstanding       # maximum allowed amount of outstanding fines
167
  current_outstanding   # current amount of outstanding fines
168
169
=cut
170
171
sub debt_renew_opac {
172
    my ($self) = @_;
173
174
    my ($amount) = C4::Members::GetMemberAccountRecords(
175
                        $self->patron->borrowernumber
176
    );
177
    my $maxoutstanding = C4::Context->preference("OPACFineNoRenewals");
178
    return $self->_debt($amount, $maxoutstanding);
179
}
180
181
=head3 exceeded_maxreserves
182
183
Returns Koha::Exceptions::Hold::MaximumHoldsReached if the total amount of
184
patron's holds exceeds the amount defined in "maxreserves" system preference.
185
186
Koha::Exceptions::Hold::MaximumHoldsReached additional fields:
187
  max_holds_allowed    # maximum amount of allowed holds (maxreserves preference)
188
  current_hold_count   # total amount of patron's current (non-found) holds
189
190
=cut
191
192
sub exceeded_maxreserves {
193
    my ($self) = @_;
194
195
    my $patron = $self->patron;
196
    my $max = C4::Context->preference('maxreserves');
197
    return unless $max;
198
    my $holds = Koha::Holds->search({
199
        borrowernumber => $patron->borrowernumber,
200
        found => undef,
201
    })->count();
202
    if ($holds && $max && $holds >= $max) {
203
        return Koha::Exceptions::Hold::MaximumHoldsReached->new(
204
            max_holds_allowed => 0+$max,
205
            current_hold_count => 0+$holds,
206
        );
207
    }
208
    return;
209
}
210
211
=head3 expired
212
213
Returns Koha::Exceptions::Patron::CardExpired if patron's card has been expired.
214
215
Koha::Exceptions::Patron::CardExpired additional fields:
216
  expiration_date
217
218
=cut
219
220
sub expired {
221
    my ($self) = @_;
222
223
    my $patron = $self->patron;
224
    if ($patron->is_expired) {
225
        return Koha::Exceptions::Patron::CardExpired->new(
226
            expiration_date => $patron->dateexpiry
227
        );
228
    }
229
    return;
230
}
231
232
=head3 from_another_library
233
234
Returns Koha::Exceptions::Patron::FromAnotherLibrary if patron is from another
235
library than currently logged-in user. System preference "IndependentBranches"
236
is considered for this method.
237
238
Koha::Exceptions::Patron::FromAnotherLibrary additional fields:
239
  patron_branch             # patron's library
240
  current_branch            # library of currently logged-in user
241
242
=cut
243
244
sub from_another_library {
245
    my ($self) = @_;
246
247
    my $patron = $self->patron;
248
    if (C4::Context->preference("IndependentBranches")) {
249
        my $userenv = C4::Context->userenv;
250
        unless (C4::Context->IsSuperLibrarian()) {
251
            if ($patron->branchcode ne $userenv->{branch}) {
252
                return Koha::Exceptions::Patron::FromAnotherLibrary->new(
253
                    patron_branch => $patron->branchcode,
254
                    current_branch => $userenv->{branch},
255
                );
256
            }
257
        }
258
    }
259
    return;
260
}
261
262
=head3 gonenoaddress
263
264
Returns Koha::Exceptions::Patron::GoneNoAddress if patron is gonenoaddress.
265
266
=cut
267
268
sub gonenoaddress {
269
    my ($self) = @_;
270
271
    if ($self->patron->gonenoaddress) {
272
        return Koha::Exceptions::Patron::GoneNoAddress->new;
273
    }
274
    return;
275
}
276
277
=head3 lost
278
279
Returns Koha::Exceptions::Patron::CardLost if patron's card is marked as lost.
280
281
=cut
282
283
sub lost {
284
    my ($self) = @_;
285
286
    if ($self->patron->lost) {
287
        return Koha::Exceptions::Patron::CardLost->new;
288
    }
289
    return;
290
}
291
292
=head3 overdue_checkouts
293
294
Returns Koha::Exceptions::Patron::DebarredOverdue if patron is debarred because
295
they have overdue checkouts.
296
297
Koha::Exceptions::Patron::DebarredOverdue additional fields:
298
  number_of_overdues
299
300
=cut
301
302
sub overdue_checkouts {
303
    my ($self) = @_;
304
305
    my $number;
306
    if ($number = $self->patron->has_overdues) {
307
        return Koha::Exceptions::Patron::DebarredOverdue->new(
308
            number_of_overdues => 0+$number,
309
        );
310
    }
311
    return;
312
}
313
314
sub _debt {
315
    my ($self, $amount, $maxoutstanding) = @_;
316
    return unless $maxoutstanding;
317
    if ($amount && $maxoutstanding && $amount > $maxoutstanding) {
318
        return Koha::Exceptions::Patron::Debt->new(
319
            max_outstanding => 0+sprintf("%.2f", $maxoutstanding),
320
            current_outstanding => 0+sprintf("%.2f", $amount),
321
        );
322
    }
323
    return;
324
}
325
326
1;
(-)a/Koha/Schema/Result/Reserve.pm (+22 lines)
Lines 317-322 __PACKAGE__->belongs_to( Link Here
317
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2GCET9quFpUvzuN7MUWZNw
317
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2GCET9quFpUvzuN7MUWZNw
318
318
319
__PACKAGE__->belongs_to(
319
__PACKAGE__->belongs_to(
320
  "borrower",
321
  "Koha::Schema::Result::Borrower",
322
  { borrowernumber => "borrowernumber" },
323
  {
324
    is_deferrable => 1,
325
    join_type     => "LEFT",
326
    on_delete     => "CASCADE",
327
    on_update     => "CASCADE",
328
  },
329
);
330
331
__PACKAGE__->belongs_to(
320
  "item",
332
  "item",
321
  "Koha::Schema::Result::Item",
333
  "Koha::Schema::Result::Item",
322
  { itemnumber => "itemnumber" },
334
  { itemnumber => "itemnumber" },
Lines 345-348 __PACKAGE__->add_columns( Link Here
345
    '+suspend' => { is_boolean => 1 }
357
    '+suspend' => { is_boolean => 1 }
346
);
358
);
347
359
360
__PACKAGE__->belongs_to(
361
  "biblioitem",
362
  "Koha::Schema::Result::Biblioitem",
363
  { biblionumber => "biblionumber" },
364
  {
365
    is_deferrable => 1,
366
    join_type     => "LEFT",
367
  },
368
);
369
348
1;
370
1;
(-)a/t/db_dependent/Koha/Availability/Checks/Biblio.t (+144 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 => 3;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
26
use C4::Members;
27
28
use Koha::Biblios;
29
use Koha::Database;
30
use Koha::Items;
31
32
use Koha::Availability::Checks::Biblio;
33
34
my $schema = Koha::Database->new->schema;
35
$schema->storage->txn_begin;
36
37
my $dbh = C4::Context->dbh;
38
$dbh->{RaiseError} = 1;
39
40
my $builder = t::lib::TestBuilder->new;
41
42
set_default_system_preferences();
43
set_default_circulation_rules();
44
45
subtest 'checked_out' => \&t_checked_out;
46
sub t_checked_out {
47
    plan tests => 2;
48
49
    my $patron = build_a_test_patron();
50
    my $item = build_a_test_item();
51
    my $biblio = Koha::Biblios->find($item->biblionumber);
52
    my $item2 = build_a_test_item();
53
    $item2->set({
54
        biblionumber => $item->biblionumber,
55
        biblioitemnumber => $item->biblioitemnumber,
56
    })->store;
57
    issue_item($item, $patron);
58
    my $bibcalc = Koha::Availability::Checks::Biblio->new($biblio);
59
    my $expecting = 'Koha::Exceptions::Biblio::CheckedOut';
60
61
    is(Koha::Checkouts->search({ itemnumber => $item->itemnumber })->count, 1,
62
       'I found out that item is checked out.');
63
    is(ref($bibcalc->checked_out($patron)), $expecting, $expecting);
64
};
65
66
subtest 'forbid_holds_on_patrons_possessions' => \&t_forbid_holds_on_patrons_possessions;
67
sub t_forbid_holds_on_patrons_possessions {
68
    plan tests => 5;
69
70
    my $patron = build_a_test_patron();
71
    my $item = build_a_test_item();
72
    my $biblio = Koha::Biblios->find($item->biblionumber);
73
    my $item2 = build_a_test_item();
74
    $item2->set({
75
        biblionumber => $item->biblionumber,
76
        biblioitemnumber => $item->biblioitemnumber,
77
    })->store;
78
    issue_item($item, $patron);
79
    my $bibcalc = Koha::Availability::Checks::Biblio->new($biblio);
80
    my $expecting = 'Koha::Exceptions::Biblio::CheckedOut';
81
82
    t::lib::Mocks::mock_preference('AllowHoldsOnPatronsPossessions', 0);
83
    is(C4::Context->preference('AllowHoldsOnPatronsPossessions'), 0,
84
       'System preference AllowHoldsOnPatronsPossessions is disabled.');
85
    is(Koha::Checkouts->search({ itemnumber => $item->itemnumber })->count, 1,
86
       'I found out that item is checked out.');
87
    is(ref($bibcalc->forbid_holds_on_patrons_possessions($patron)), $expecting,
88
       $expecting);
89
    t::lib::Mocks::mock_preference('AllowHoldsOnPatronsPossessions', 1);
90
    is(C4::Context->preference('AllowHoldsOnPatronsPossessions'), 1,
91
       'Given system preference AllowHoldsOnPatronsPossessions is enabled,');
92
    ok(!$bibcalc->forbid_holds_on_patrons_possessions($patron), 'When I check'
93
       .' biblio forbid holds on patrons possesions, no exception is given.');
94
};
95
96
subtest 'forbid_multiple_issues' => \&t_forbid_multiple_issues;
97
sub t_forbid_multiple_issues {
98
    plan tests => 4;
99
100
    my $patron = build_a_test_patron();
101
    my $item = build_a_test_item();
102
    my $biblio = Koha::Biblios->find($item->biblionumber);
103
    my $item2 = build_a_test_item();
104
    $item2->set({
105
        biblionumber => $item->biblionumber,
106
        biblioitemnumber => $item->biblioitemnumber,
107
    })->store;
108
    issue_item($item2, $patron);
109
    my $checkoutcalc = Koha::Availability::Checks::Biblio->new($biblio);
110
111
    is($item->biblionumber, $item2->biblionumber, 'Item one and item two belong'
112
        .' to the same biblio.');
113
    is(Koha::Checkouts->find({ itemnumber => $item2->itemnumber})->borrowernumber,
114
       $patron->borrowernumber, 'Item two seems to be issued to me.');
115
116
    subtest 'Given AllowMultipleIssuesOnABiblio is enabled' => sub {
117
        plan tests => 2;
118
119
        t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 1);
120
        is(C4::Context->preference('AllowMultipleIssuesOnABiblio'), 1,
121
           'System preference AllowMultipleIssuesOnABiblio is enabled.');
122
        is($checkoutcalc->forbid_multiple_issues($patron), undef,
123
           'When I ask if there is another biblio already checked out,'
124
           .' then no exception is returned.');
125
    };
126
127
    subtest 'Given AllowMultipleIssuesOnABiblio is disabled' => sub {
128
        plan tests => 3;
129
130
        my $expecting = 'Koha::Exceptions::Biblio::CheckedOut';
131
        my $returned;
132
133
        t::lib::Mocks::mock_preference('AllowMultipleIssuesOnABiblio', 0);
134
        is(C4::Context->preference('AllowMultipleIssuesOnABiblio'), 0,
135
           'System preference AllowMultipleIssuesOnABiblio is disabled.');
136
        ok($returned = ref($checkoutcalc->forbid_multiple_issues($patron)),
137
           'When I ask if there is another biblio already checked out,');
138
        is ($returned, $expecting, "then exception $expecting is returned.");
139
    };
140
};
141
142
$schema->storage->txn_rollback;
143
144
1;
(-)a/t/db_dependent/Koha/Availability/Checks/Biblioitem.t (+83 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 => 2;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
26
use Koha::Biblioitems;
27
use Koha::Database;
28
use Koha::DateUtils;
29
use Koha::Items;
30
31
use Koha::Availability::Checks::Biblioitem;
32
33
my $schema = Koha::Database->new->schema;
34
$schema->storage->txn_begin;
35
36
my $dbh = C4::Context->dbh;
37
$dbh->{RaiseError} = 1;
38
39
my $builder = t::lib::TestBuilder->new;
40
41
set_default_system_preferences();
42
set_default_circulation_rules();
43
44
subtest 'age_restricted, given patron is old enough' => \&t_old_enough;
45
sub t_old_enough {
46
    plan tests => 3;
47
48
    my $item = build_a_test_item();
49
    my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
50
    $biblioitem->set({ agerestriction => 'PEGI 18' })->store;
51
    my $patron = build_a_test_patron();
52
    my $bibitemcalc = Koha::Availability::Checks::Biblioitem->new($biblioitem);
53
54
    is($patron->dateofbirth, '1950-10-10', 'Patron says he is born in the 1950s.');
55
    is($biblioitem->agerestriction, 'PEGI 18', 'Item is restricted for under 18.');
56
    ok(!$bibitemcalc->age_restricted($patron), 'When I check for age restriction, then no exception is given.');
57
};
58
59
subtest 'age_restricted, given patron is too young' => \&t_too_young;
60
sub t_too_young {
61
    plan tests => 4;
62
63
    my $item = build_a_test_item();
64
    my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
65
    $biblioitem->set({ agerestriction => 'PEGI 18' })->store;
66
    my $patron = build_a_test_patron();
67
    my $now = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
68
    $patron->set({ dateofbirth => $now })->store;
69
    my $bibitemcalc = Koha::Availability::Checks::Biblioitem->new($biblioitem);
70
    my $expecting = 'Koha::Exceptions::Patron::AgeRestricted';
71
72
    is($patron->dateofbirth, $now, 'Patron says he is born today.');
73
    my $reason = $bibitemcalc->age_restricted($patron);
74
    is($biblioitem->agerestriction, 'PEGI 18', 'Biblio item is restricted for under 18.');
75
    is(ref($reason), $expecting,
76
       "When I check for age restriction, then exception $expecting is given.");
77
    is($reason->age_restriction, 'PEGI 18', 'The reason also specifies the'
78
       .' age restriction, PEGI 18.');
79
};
80
81
$schema->storage->txn_rollback;
82
83
1;
(-)a/t/db_dependent/Koha/Availability/Checks/Checkout.t (+113 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
26
use C4::Members;
27
28
use Koha::Biblios;
29
use Koha::Database;
30
use Koha::Items;
31
32
use Koha::Availability::Checks::Checkout;
33
34
my $schema = Koha::Database->new->schema;
35
$schema->storage->txn_begin;
36
37
my $dbh = C4::Context->dbh;
38
$dbh->{RaiseError} = 1;
39
40
my $builder = t::lib::TestBuilder->new;
41
42
set_default_system_preferences();
43
set_default_circulation_rules();
44
45
subtest 'invalid_due_date, invalid due date' => \&t_invalid_due_date;
46
sub t_invalid_due_date {
47
    plan tests => 1;
48
49
    my $patron = build_a_test_patron();
50
    my $item = build_a_test_item();
51
52
    my $duedate = 'invalid';
53
    my $checkoutcalc = Koha::Availability::Checks::Checkout->new;
54
    my $expecting = 'Koha::Exceptions::Checkout::InvalidDueDate';
55
    is(ref($checkoutcalc->invalid_due_date($item, $patron, $duedate)), $expecting,
56
       "When using duedate 'invalid', then exception $expecting is given.");
57
};
58
59
subtest 'invalid_due_date, due date before now' => \&t_invalid_due_date_before_now;
60
sub t_invalid_due_date_before_now {
61
    plan tests => 1;
62
63
    my $patron = build_a_test_patron();
64
    my $item = build_a_test_item();
65
66
    my $before = output_pref({ dt => dt_from_string()->subtract_duration(
67
                          DateTime::Duration->new(days => 100)), dateformat => 'iso', dateonly => 1 });
68
    my $checkoutcalc = Koha::Availability::Checks::Checkout->new;
69
    my $expecting = 'Koha::Exceptions::Checkout::DueDateBeforeNow';
70
    is(ref($checkoutcalc->invalid_due_date($item, $patron, $before)), $expecting,
71
       "When using duedate that is in the past, then exception $expecting is given.");
72
};
73
74
subtest 'invalid_due_date, good due date' => \&t_invalid_due_date_not_invalid;
75
sub t_invalid_due_date_not_invalid {
76
    plan tests => 1;
77
78
    my $patron = build_a_test_patron();
79
    my $item = build_a_test_item();
80
81
    my $before = output_pref({ dt => dt_from_string()->add_duration(
82
                          DateTime::Duration->new(days => 14)), dateformat => 'iso', dateonly => 1 });
83
    my $checkoutcalc = Koha::Availability::Checks::Checkout->new;
84
    is($checkoutcalc->invalid_due_date($item, $patron, $before), undef,
85
       'When using a duedate that is in two weeks from now, then no exception is given.');
86
};
87
88
subtest 'no_more_renewals' => \&t_no_more_renewals;
89
sub t_no_more_renewals {
90
    plan tests => 2;
91
92
    my $patron = build_a_test_patron();
93
    my $item = build_a_test_item();
94
95
    issue_item($item, $patron);
96
    Koha::IssuingRules->search->delete;
97
    my $rule = Koha::IssuingRule->new({
98
        branchcode   => '*',
99
        itemtype     => '*',
100
        categorycode => '*',
101
        renewalsallowed => 0,
102
    })->store;
103
    my $checkoutcalc = Koha::Availability::Checks::Checkout->new;
104
    my $expecting = 'Koha::Exceptions::Checkout::NoMoreRenewals';
105
    my $issue = Koha::Checkouts->find({ itemnumber => $item->itemnumber});
106
    is($issue->borrowernumber, $patron->borrowernumber, 'Item seems to be issued to me.');
107
    is(ref($checkoutcalc->no_more_renewals($issue)), $expecting,
108
       "When checking for no more renewals, then exception $expecting is given.");
109
};
110
111
$schema->storage->txn_rollback;
112
113
1;
(-)a/t/db_dependent/Koha/Availability/Checks/IssuingRule.t (+513 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 => 13;
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::Availability::Checks::IssuingRule;
32
33
my $schema = Koha::Database->new->schema;
34
$schema->storage->txn_begin;
35
36
my $builder = t::lib::TestBuilder->new;
37
38
subtest 'maximum_checkouts_reached' => \&t_maximum_checkouts_reached;
39
sub t_maximum_checkouts_reached {
40
    plan tests => 5;
41
42
    set_default_system_preferences();
43
    set_default_circulation_rules();
44
    my $item = build_a_test_item();
45
    my $patron = build_a_test_patron();
46
    Koha::IssuingRules->search->delete;
47
    my $rule = Koha::IssuingRule->new({
48
        branchcode   => '*',
49
        itemtype     => $item->effective_itemtype,
50
        categorycode => '*',
51
        maxissueqty => 1,
52
    })->store;
53
54
    issue_item($item, $patron);
55
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
56
        item => $item,
57
        patron => $patron,
58
    });
59
    my $expecting = 'Koha::Exceptions::Checkout::MaximumCheckoutsReached';
60
    my $max_checkouts_reached = $issuingcalc->maximum_checkouts_reached;
61
    is($rule->maxissueqty, 1, 'When I look at issuing rules, I see that'
62
       .' maxissueqty is 1 for this itemtype.');
63
    is(Koha::Checkouts->search({borrowernumber=>$patron->borrowernumber})->count,
64
       1, 'I see that I have one checkout.');
65
    is(ref($max_checkouts_reached), $expecting, "When I ask if"
66
       ." maximum checkouts is reached, exception $expecting is given.");
67
    is($max_checkouts_reached->max_checkouts_allowed, $rule->maxissueqty,
68
       'Exception says max checkouts allowed is same as maxissueqty.');
69
    is($max_checkouts_reached->current_checkout_count, 1,
70
       'Exception says I currently have one checkout.');
71
};
72
73
subtest 'maximum_checkouts_reached, not reached' => \&t_maximum_checkouts_not_reached;
74
sub t_maximum_checkouts_not_reached {
75
    plan tests => 3;
76
77
    set_default_system_preferences();
78
    set_default_circulation_rules();
79
    my $item = build_a_test_item();
80
    my $patron = build_a_test_patron();
81
    Koha::IssuingRules->search->delete;
82
    my $rule = Koha::IssuingRule->new({
83
        branchcode   => '*',
84
        itemtype     => $item->effective_itemtype,
85
        categorycode => '*',
86
        maxissueqty => 2,
87
    })->store;
88
89
    issue_item($item, $patron);
90
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
91
        item => $item,
92
        patron => $patron,
93
    });
94
95
    is($rule->maxissueqty, 2, 'When I look at issuing rules, I see that'
96
       .' maxissueqty is 2 for this itemtype.');
97
    is(Koha::Checkouts->search({borrowernumber=>$patron->borrowernumber})->count,
98
       1, 'I see that I have one checkout.');
99
    ok(!$issuingcalc->maximum_checkouts_reached, 'When I ask if'
100
       .' maximum checkouts is reached, no exception is given.');
101
};
102
103
subtest 'maximum_holds_reached' => \&t_maximum_holds_reached;
104
sub t_maximum_holds_reached {
105
    plan tests => 3;
106
107
    set_default_system_preferences();
108
    set_default_circulation_rules();
109
    my $item = build_a_test_item();
110
    my $patron = build_a_test_patron();
111
    Koha::IssuingRules->search->delete;
112
    my $rule = Koha::IssuingRule->new({
113
        branchcode   => '*',
114
        itemtype     => $item->effective_itemtype,
115
        categorycode => '*',
116
        reservesallowed => 1,
117
        holds_per_record => 2,
118
    })->store;
119
120
    add_item_level_hold($item, $patron, $item->homebranch);
121
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
122
        item => $item,
123
        patron => $patron,
124
    });
125
    my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached';
126
127
    is($rule->reservesallowed, 1, 'When I look at issuing rules, I see that'
128
       .' reservesallowed is 1 for this itemtype.');
129
    is(Koha::Holds->search({borrowernumber=>$patron->borrowernumber})->count,
130
       1, 'I see that I have one hold.');
131
    is(ref($issuingcalc->maximum_holds_reached), $expecting, "When I ask if"
132
       ." maximum holds is reached, exception $expecting is given.");
133
};
134
135
subtest 'maximum_holds_reached, not reached' => \&t_maximum_holds_not_reached;
136
sub t_maximum_holds_not_reached {
137
    plan tests => 3;
138
139
    set_default_system_preferences();
140
    set_default_circulation_rules();
141
    my $item = build_a_test_item();
142
    my $patron = build_a_test_patron();
143
    Koha::IssuingRules->search->delete;
144
    my $rule = Koha::IssuingRule->new({
145
        branchcode   => '*',
146
        itemtype     => $item->effective_itemtype,
147
        categorycode => '*',
148
        reservesallowed => 2,
149
    })->store;
150
151
    add_item_level_hold($item, $patron, $patron->branchcode);
152
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
153
        item => $item,
154
        patron => $patron,
155
    });
156
    my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached';
157
    is($rule->reservesallowed, 2, 'When I look at issuing rules, I see that'
158
       .' reservesallowed is 2 for this itemtype.');
159
    is(Koha::Holds->search({borrowernumber=>$patron->borrowernumber})->count,
160
       1, 'I see that I have one hold.');
161
    ok(!$issuingcalc->maximum_holds_reached, 'When I ask if'
162
       .' maximum holds is reached, no exception is given.');
163
};
164
165
subtest 'maximum_holds_for_record_reached' => \&t_maximum_holds_for_record_reached;
166
sub t_maximum_holds_for_record_reached {
167
    plan tests => 3;
168
169
    set_default_system_preferences();
170
    set_default_circulation_rules();
171
    my $item = build_a_test_item();
172
    my $patron = build_a_test_patron();
173
    Koha::IssuingRules->search->delete;
174
    my $rule = Koha::IssuingRule->new({
175
        branchcode   => '*',
176
        itemtype     => $item->effective_itemtype,
177
        categorycode => '*',
178
        reservesallowed => 2,
179
        holds_per_record => 1,
180
    })->store;
181
182
    add_item_level_hold($item, $patron, $item->homebranch);
183
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
184
        item => $item,
185
        patron => $patron,
186
    });
187
    my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsForRecordReached';
188
189
    is($rule->holds_per_record, 1, 'When I look at issuing rules, I see that'
190
       .' holds_per_record is 1 for this itemtype.');
191
    is(Koha::Holds->search({borrowernumber=>$patron->borrowernumber})->count,
192
       1, 'I see that I have one hold.');
193
    is(ref($issuingcalc->maximum_holds_for_record_reached), $expecting, "When I ask if"
194
       ." maximum holds for record is reached, exception $expecting is given.");
195
};
196
197
subtest 'maximum_holds_for_record_reached, not reached' => \&t_maximum_holds_for_record_not_reached;
198
sub t_maximum_holds_for_record_not_reached {
199
    plan tests => 3;
200
201
    set_default_system_preferences();
202
    set_default_circulation_rules();
203
    my $item = build_a_test_item();
204
    my $patron = build_a_test_patron();
205
    Koha::IssuingRules->search->delete;
206
    my $rule = Koha::IssuingRule->new({
207
        branchcode   => '*',
208
        itemtype     => $item->effective_itemtype,
209
        categorycode => '*',
210
        reservesallowed => 2,
211
        holds_per_record => 2,
212
    })->store;
213
214
    add_item_level_hold($item, $patron, $item->homebranch);
215
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
216
        item => $item,
217
        patron => $patron,
218
    });
219
    is($rule->holds_per_record, 2, 'When I look at issuing rules, I see that'
220
       .' holds_per_record is 2 for this itemtype.');
221
    is(Koha::Holds->search({borrowernumber=>$patron->borrowernumber})->count,
222
       1, 'I see that I have one hold.');
223
    ok(!$issuingcalc->maximum_holds_for_record_reached, 'When I ask if'
224
       .' maximum holds for record is reached, no exception is given.');
225
};
226
227
subtest 'on_shelf_holds_forbidden while on shelf holds are allowed' => \&t_on_shelf_holds_forbidden;
228
sub t_on_shelf_holds_forbidden {
229
    plan tests => 2;
230
231
    set_default_system_preferences();
232
    set_default_circulation_rules();
233
    my $item = build_a_test_item();
234
    my $item2 = build_a_test_item();
235
    Koha::IssuingRules->search->delete;
236
    my $rule = Koha::IssuingRule->new({
237
        branchcode   => '*',
238
        itemtype     => $item->effective_itemtype,
239
        categorycode => '*',
240
        holds_per_record => 1,
241
        reservesallowed => 1,
242
        onshelfholds => 1,
243
    })->store;
244
245
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
246
        item => $item,
247
    });
248
    is($rule->onshelfholds, 1, 'When I look at issuing rules, I see that'
249
       .' onshelfholds are allowed.');
250
    ok(!$issuingcalc->on_shelf_holds_forbidden, 'When I check availability calculation'
251
       .' to see if on shelf holds are forbidden, then no exception is given.');
252
};
253
254
subtest 'on_shelf_holds_forbidden if any available' => \&t_on_shelf_holds_forbidden_any_available;
255
sub t_on_shelf_holds_forbidden_any_available {
256
    plan tests => 3;
257
258
    set_default_system_preferences();
259
    set_default_circulation_rules();
260
    my $item = build_a_test_item();
261
    my $patron = build_a_test_patron();
262
    my $biblio = Koha::Biblios->find($item->biblionumber);
263
    my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
264
    my $item2 = build_a_test_item($biblio, $biblioitem);
265
    $item2->itype($item->itype)->store;
266
    Koha::IssuingRules->search->delete;
267
    my $rule = Koha::IssuingRule->new({
268
        branchcode   => '*',
269
        itemtype     => $item->effective_itemtype,
270
        categorycode => '*',
271
        holds_per_record => 1,
272
        reservesallowed => 1,
273
        onshelfholds => 2,
274
    })->store;
275
276
    my $expecting = 'Koha::Exceptions::Hold::OnShelfNotAllowed';
277
    subtest 'While there are available items' => sub {
278
        plan tests => 4;
279
        my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
280
            item => $item,
281
        });
282
        is($rule->onshelfholds, 2, 'When I look at issuing rules, I see that'
283
           .' onshelfholds are allowed if all are unavailable.');
284
        ok(!$item->onloan && !$item2->onloan, 'Both items are available.');
285
        ok($issuingcalc->on_shelf_holds_forbidden, 'When I check '
286
           .'availability calculation to see if on shelf holds are forbidden,');
287
        is(ref($issuingcalc->on_shelf_holds_forbidden), $expecting, 'Then'
288
           ." exception $expecting is given.");
289
    };
290
    subtest 'While one of two items is available' => sub {
291
        plan tests => 7;
292
        is($rule->onshelfholds, 2, 'When I look at issuing rules, I see that'
293
           .' onshelfholds are allowed if all are unavailable.');
294
        ok(issue_item($item, $patron), 'We have issued one of two items.');
295
        my $issuingcalc;
296
        ok($issuingcalc = Koha::Availability::Checks::IssuingRule->new({
297
            item => $item
298
        }), 'First, we will check on shelf hold restrictions for the item that is'
299
           .' unavailable.');
300
        ok($issuingcalc->on_shelf_holds_forbidden, 'When I check '
301
           .'availability calculation to see if on shelf holds are forbidden,');
302
        is(ref($issuingcalc->on_shelf_holds_forbidden), $expecting, 'Then'
303
           ." exception $expecting is given.");
304
        ok($issuingcalc = Koha::Availability::Checks::IssuingRule->new({
305
            item => $item2
306
        }), 'Then, we will check on shelf hold restrictions for the item that is'
307
           .' available.');
308
        is(ref($issuingcalc->on_shelf_holds_forbidden), $expecting, 'When I check '
309
           .'availability calculation to see if on shelf holds are forbidden, then'
310
           ." exception $expecting is given.");
311
    };
312
    subtest 'While all are unavailable' => sub {
313
        plan tests => 3;
314
        my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
315
            item => $item,
316
        });
317
        is($rule->onshelfholds, 2, 'When I look at issuing rules, I see that'
318
           .' onshelfholds are allowed if all are unavailable.');
319
        ok(issue_item($item2, $patron), 'Both items are now unavailable.');
320
        ok(!$issuingcalc->on_shelf_holds_forbidden, 'When I check availability calculation'
321
       .' to see if on shelf holds are forbidden, then no exception is given.');
322
    };
323
};
324
325
subtest 'on_shelf_holds_forbidden if all available' => \&t_on_shelf_holds_forbidden_all_available;
326
sub t_on_shelf_holds_forbidden_all_available {
327
    plan tests => 3;
328
329
    set_default_system_preferences();
330
    set_default_circulation_rules();
331
    my $item = build_a_test_item();
332
    my $patron = build_a_test_patron();
333
    my $biblio = Koha::Biblios->find($item->biblionumber);
334
    my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
335
    my $item2 = build_a_test_item($biblio, $biblioitem);
336
    $item2->itype($item->itype)->store;
337
    Koha::IssuingRules->search->delete;
338
    my $rule = Koha::IssuingRule->new({
339
        branchcode   => '*',
340
        itemtype     => $item->effective_itemtype,
341
        categorycode => '*',
342
        holds_per_record => 1,
343
        reservesallowed => 1,
344
        onshelfholds => 0,
345
    })->store;
346
347
    my $expecting = 'Koha::Exceptions::Hold::OnShelfNotAllowed';
348
    subtest 'While all items are available' => sub {
349
        plan tests => 3;
350
        my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
351
            item => $item,
352
        });
353
        is($rule->onshelfholds, 0, 'When I look at issuing rules, I see that'
354
           .' onshelfholds are allowed if any is unavailable.');
355
        ok($issuingcalc->on_shelf_holds_forbidden,'When I check '
356
           .'availability calculation to see if on shelf holds are forbidden,');
357
        is(ref($issuingcalc->on_shelf_holds_forbidden), $expecting, 'Then'
358
           ." exception $expecting is given.");
359
    };
360
    subtest 'While one of two items is available' => sub {
361
        plan tests => 7;
362
        is($rule->onshelfholds, 0, 'When I look at issuing rules, I see that'
363
           .' onshelfholds are allowed if any is unavailable.');
364
        ok(issue_item($item, $patron), 'We have issued one of two items.');
365
        $item = Koha::Items->find($item->itemnumber); #refresh
366
        my $issuingcalc;
367
        ok($issuingcalc = Koha::Availability::Checks::IssuingRule->new({
368
            item => $item
369
        }), 'First, we will check on shelf hold restrictions for the item that is'
370
           .' unavailable.');
371
        ok(!$issuingcalc->on_shelf_holds_forbidden, 'When I check availability calculation'
372
        .' to see if on shelf holds are forbidden, then no exception is given.');
373
        ok($issuingcalc = Koha::Availability::Checks::IssuingRule->new({
374
            item => $item2
375
        }), 'Then, we will check on shelf hold restrictions for the item that is'
376
           .' available.');
377
        ok($issuingcalc->on_shelf_holds_forbidden, 'When I check '
378
           .'availability calculation to see if on shelf holds are forbidden,');
379
        is(ref($issuingcalc->on_shelf_holds_forbidden), $expecting, 'Then'
380
           ." exception $expecting is given.");
381
    };
382
    subtest 'While all are unavailable' => sub {
383
        plan tests => 3;
384
        my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
385
            item => $item,
386
        });
387
        is($rule->onshelfholds, 0, 'When I look at issuing rules, I see that'
388
           .' onshelfholds are allowed if all are unavailable.');
389
        ok(issue_item($item2, $patron), 'Both items are now unavailable.');
390
        ok(!$issuingcalc->on_shelf_holds_forbidden, 'When I check availability calculation'
391
       .' to see if on shelf holds are forbidden, then no exception is given.');
392
    };
393
};
394
395
subtest 'opac_item_level_hold_forbidden' => \&t_opac_item_level_hold_forbidden;
396
sub t_opac_item_level_hold_forbidden {
397
    plan tests => 2;
398
399
    set_default_system_preferences();
400
    set_default_circulation_rules();
401
    my $item = build_a_test_item();
402
    Koha::IssuingRules->search->delete;
403
    my $rule = Koha::IssuingRule->new({
404
        branchcode   => '*',
405
        itemtype     => $item->effective_itemtype,
406
        categorycode => '*',
407
        holds_per_record => 1,
408
        reservesallowed => 1,
409
        opacitemholds => 'N',
410
    })->store;
411
412
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
413
        item => $item,
414
    });
415
    my $expecting = 'Koha::Exceptions::Hold::ItemLevelHoldNotAllowed';
416
    is($rule->opacitemholds, 'N', 'When I look at issuing rules, I see that'
417
       .' opacitemholds is disabled for this itemtype');
418
    is(ref($issuingcalc->opac_item_level_hold_forbidden), $expecting, "When I ask if"
419
       ." item level holds are allowed, exception $expecting is given.");
420
};
421
422
subtest 'opac_item_level_hold_forbidden, not forbidden' => \&t_opac_item_level_hold_not_forbidden;
423
sub t_opac_item_level_hold_not_forbidden {
424
    plan tests => 2;
425
426
    set_default_system_preferences();
427
    set_default_circulation_rules();
428
    my $item = build_a_test_item();
429
    Koha::IssuingRules->search->delete;
430
    my $rule = Koha::IssuingRule->new({
431
        branchcode   => '*',
432
        itemtype     => $item->effective_itemtype,
433
        categorycode => '*',
434
        holds_per_record => 1,
435
        reservesallowed => 1,
436
        opacitemholds => 'Y',
437
    })->store;
438
439
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
440
        item => $item,
441
    });
442
    my $expecting = 'Koha::Exceptions::Hold::ItemLevelHoldNotAllowed';
443
    is($rule->opacitemholds, 'Y', 'When I look at issuing rules, I see that'
444
       .' opacitemholds is enabled for this itemtype');
445
    ok(!$issuingcalc->opac_item_level_hold_forbidden, 'When I ask if'
446
       .' item level holds are allowed, then no exception is given.');
447
};
448
449
subtest 'zero_holds_allowed' => \&t_zero_holds_allowed;
450
sub t_zero_holds_allowed {
451
    plan tests => 3;
452
453
    set_default_system_preferences();
454
    set_default_circulation_rules();
455
    my $item = build_a_test_item();
456
    my $patron = build_a_test_patron();
457
    Koha::IssuingRules->search->delete;
458
    my $rule = Koha::IssuingRule->new({
459
        branchcode   => '*',
460
        itemtype     => $item->effective_itemtype,
461
        categorycode => '*',
462
        reservesallowed => 0,
463
    })->store;
464
465
    add_item_level_hold($item, $patron, $item->homebranch);
466
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
467
        item => $item,
468
        patron => $patron,
469
    });
470
    my $expecting = 'Koha::Exceptions::Hold::ZeroHoldsAllowed';
471
472
    is($rule->reservesallowed, 0, 'When I look at issuing rules, I see that'
473
       .' zero holds are allowed for this itemtype.');
474
    is(Koha::Holds->search({borrowernumber=>$patron->borrowernumber})->count,
475
       1, 'I see that I have one hold.');
476
    is(ref($issuingcalc->zero_holds_allowed), $expecting, "When I ask if"
477
       ." zero holds are allowed, exception $expecting is given.");
478
};
479
480
subtest 'zero_checkouts_allowed' => \&t_zero_checkouts_allowed;
481
sub t_zero_checkouts_allowed {
482
    plan tests => 3;
483
484
    set_default_system_preferences();
485
    set_default_circulation_rules();
486
    my $item = build_a_test_item();
487
    my $patron = build_a_test_patron();
488
    Koha::IssuingRules->search->delete;
489
    my $rule = Koha::IssuingRule->new({
490
        branchcode   => '*',
491
        itemtype     => $item->effective_itemtype,
492
        categorycode => '*',
493
        maxissueqty  => 0,
494
    })->store;
495
496
    issue_item($item, $patron);
497
    my $issuingcalc = Koha::Availability::Checks::IssuingRule->new({
498
        item => $item,
499
        patron => $patron,
500
    });
501
    my $expecting = 'Koha::Exceptions::Checkout::ZeroCheckoutsAllowed';
502
503
    is($rule->maxissueqty, 0, 'When I look at issuing rules, I see that'
504
       .' zero checkouts are allowed for this itemtype.');
505
    is(Koha::Checkouts->search({borrowernumber=>$patron->borrowernumber})->count,
506
       1, 'I see that I have one checkout.');
507
    is(ref($issuingcalc->zero_checkouts_allowed), $expecting, "When I ask if"
508
       ." zero checkouts are allowed, exception $expecting is given.");
509
};
510
511
$schema->storage->txn_rollback;
512
513
1;
(-)a/t/db_dependent/Koha/Availability/Checks/Item.t (+385 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 => 14;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
26
use C4::Members;
27
28
use Koha::Account::Lines;
29
use Koha::Biblioitems;
30
use Koha::Database;
31
use Koha::DateUtils;
32
use Koha::Items;
33
use Koha::Item::Transfers;
34
35
use Koha::Availability::Checks::Item;
36
37
my $schema = Koha::Database->new->schema;
38
$schema->storage->txn_begin;
39
40
my $dbh = C4::Context->dbh;
41
$dbh->{RaiseError} = 1;
42
43
my $builder = t::lib::TestBuilder->new;
44
45
set_default_system_preferences();
46
set_default_circulation_rules();
47
48
subtest 'checked_out' => \&t_checked_out;
49
sub t_checked_out {
50
    plan tests => 2;
51
52
    my $patron = build_a_test_patron();
53
    my $item = build_a_test_item();
54
    issue_item($item, $patron);
55
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
56
    my $expecting = 'Koha::Exceptions::Item::CheckedOut';
57
58
    is(Koha::Checkouts->search({ itemnumber => $item->itemnumber })->count, 1,
59
       'I found out that item is checked out.');
60
    is(ref($itemcalc->checked_out), $expecting, "When I check item availability"
61
       ." calculation for checked_out, then exception $expecting is given.");
62
};
63
64
subtest 'damaged' => \&t_damaged;
65
sub t_damaged {
66
    plan tests => 2;
67
68
    t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0);
69
70
    my $item = build_a_test_item()->set({damaged=>1})->store;
71
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
72
    my $expecting = 'Koha::Exceptions::Item::Damaged';
73
74
    is($item->damaged, 1, 'When I look at the item, I see that it is damaged.');
75
    is(ref($itemcalc->damaged), $expecting, "When I check item availability"
76
       ." calculation for damaged, then exception $expecting is given.");
77
};
78
79
80
subtest 'from_another_branch, item from same branch than me'
81
=> \&t_from_another_library_same_branch;
82
sub t_from_another_library_same_branch {
83
    plan tests => 3;
84
85
    t::lib::Mocks::mock_preference('IndependentBranches', 1);
86
    t::lib::Mocks::mock_preference('HomeOrHoldingBranch', 'homebranch');
87
88
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
89
90
    C4::Context->_new_userenv('xxx');
91
    C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode,
92
                             'Midway Public Library', undef, '', '');
93
94
    my $item = build_a_test_item();
95
    $item->homebranch($branchcode)->store;
96
97
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
98
99
    is(C4::Context->userenv->{branch}, $item->homebranch,
100
       'Patron is from same branch as me.');
101
    ok(C4::Context->preference('IndependentBranches'),
102
       'IndependentBranches system preference is on.');
103
    ok(!$itemcalc->from_another_library, 'When I check if item is'
104
       .' from same branch as me, then no exception is given.');
105
}
106
107
subtest 'from_another_library, item from different branch than me'
108
=> \&t_from_another_library;
109
sub t_from_another_library {
110
    plan tests => 3;
111
112
    t::lib::Mocks::mock_preference('IndependentBranches', 1);
113
    t::lib::Mocks::mock_preference('HomeOrHoldingBranch', 'homebranch');
114
115
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
116
    my $branchcode2 = $builder->build({ source => 'Branch' })->{'branchcode'};
117
118
    C4::Context->_new_userenv('xxx');
119
    C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode,
120
                             'Midway Public Library', undef, '', '');
121
122
    my $item = build_a_test_item();
123
    $item->homebranch($branchcode2)->store;
124
125
    my $itemcalc= Koha::Availability::Checks::Item->new($item);
126
    my $expecting = 'Koha::Exceptions::Item::FromAnotherLibrary';
127
128
    isnt(C4::Context->userenv->{branch}, $item->homebranch, 'Item is not from'
129
         .' same branch as me.');
130
    ok(C4::Context->preference('IndependentBranches'), 'IndependentBranches'
131
       .' system preference is on.');
132
    is(ref($itemcalc->from_another_library), $expecting, 'When I check if item is'
133
       ." from same branch as me, then $expecting is given.");
134
}
135
136
subtest 'held' => \&t_held;
137
sub t_held {
138
    plan tests => 2;
139
140
    my $patron = build_a_test_patron();
141
    my $item = build_a_test_item();
142
    add_item_level_hold($item, $patron, $patron->branchcode);
143
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
144
    my $expecting = 'Koha::Exceptions::Item::Held';
145
146
    is(Koha::Holds->search({biblionumber => $item->biblionumber})->count, 1,
147
       'I found out that item is held.');
148
    is(ref($itemcalc->held), $expecting, "When I check item availability "
149
       ."calculation for held, then exception $expecting is given.");
150
};
151
152
subtest 'held_by_patron' => \&t_held_by_patron;
153
sub t_held_by_patron {
154
    plan tests => 2;
155
156
    my $patron = build_a_test_patron();
157
    my $item = build_a_test_item();
158
    add_item_level_hold($item, $patron, $patron->branchcode);
159
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
160
    my $expecting = 'Koha::Exceptions::Item::AlreadyHeldForThisPatron';
161
162
    is(Koha::Holds->search({biblionumber => $item->biblionumber})->count, 1,
163
       'I found out that item is held.');
164
    is(ref($itemcalc->held_by_patron($patron)), $expecting, "When I check item "
165
       ."availability calculation for held, then exception $expecting is given.");
166
};
167
168
subtest 'lost' => \&t_lost;
169
sub t_lost {
170
    plan tests => 2;
171
172
    my $item = build_a_test_item()->set({itemlost=>1})->store;
173
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
174
    my $expecting = 'Koha::Exceptions::Item::Lost';
175
176
    is($item->itemlost, 1, 'When I look at the item, I see that it is lost.');
177
    is(ref($itemcalc->lost), $expecting, "When I check availability calculation"
178
       ." for item lost, then exception $expecting is given.");
179
};
180
181
subtest 'notforloan' => \&t_notforloan;
182
sub t_notforloan {
183
    plan tests => 2;
184
185
    subtest 'Given item is notforloan > 0' => sub {
186
        plan tests => 2;
187
188
        my $item = build_a_test_item()->set({notforloan=>1})->store;
189
        my $itemcalc = Koha::Availability::Checks::Item->new($item);
190
        my $expecting = 'Koha::Exceptions::Item::NotForLoan';
191
192
        is($item->notforloan, 1, 'When I look at the item, I see that it is not'
193
           .' for loan.');
194
        is(ref($itemcalc->notforloan), $expecting,
195
            "When I look availability calculation for at item notforloan, then "
196
            ." exception $expecting is given.");
197
    };
198
199
    subtest 'Given item type is notforloan > 0' => sub {
200
        subtest 'Given item-level_itypes is on (item-itemtype)'
201
        => \&t_itemlevel_itemtype_notforloan_item_level_itypes_on;
202
        subtest 'Given item-level_itypes is off (biblioitem-itemtype)'
203
        => \&t_itemlevel_itemtype_notforloan_item_level_itypes_off;
204
        sub t_itemlevel_itemtype_notforloan_item_level_itypes_on {
205
            plan tests => 3;
206
207
            t::lib::Mocks::mock_preference('item-level_itypes', 1);
208
209
            my $item = build_a_test_item();
210
            my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
211
            my $itemtype = Koha::ItemTypes->find($item->itype);
212
            $itemtype->set({notforloan=>1})->store;
213
            my $itemcalc = Koha::Availability::Checks::Item->new($item);
214
            my $expecting = 'Koha::Exceptions::ItemType::NotForLoan';
215
216
            is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 0,
217
               'Biblioitem itemtype is for loan.');
218
            is(Koha::ItemTypes->find($item->itype)->notforloan, 1,
219
               'Item itemtype is not for loan.');
220
            is(ref($itemcalc->notforloan), $expecting, "When I look at "
221
               ."availability calculation for item notforloan, then "
222
               ."exception $expecting is given.");
223
        };
224
        sub t_itemlevel_itemtype_notforloan_item_level_itypes_off {
225
            plan tests => 3;
226
227
            t::lib::Mocks::mock_preference('item-level_itypes', 0);
228
229
            my $item = build_a_test_item();
230
            my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber);
231
            my $itemtype = Koha::ItemTypes->find($biblioitem->itemtype);
232
            $itemtype->set({notforloan=>1})->store;
233
            my $itemcalc = Koha::Availability::Checks::Item->new($item);
234
            my $expecting = 'Koha::Exceptions::ItemType::NotForLoan';
235
236
            is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 1,
237
               'Biblioitem itemtype is not for loan.');
238
            is(Koha::ItemTypes->find($item->itype)->notforloan, 0,
239
               'Item itemtype is for loan.');
240
            is(ref($itemcalc->notforloan), $expecting, "When I look at "
241
               ."availability calculation for item notforloan, then "
242
               ."exception $expecting is given.");
243
        };
244
    };
245
};
246
247
subtest 'onloan' => \&t_onloan;
248
sub t_onloan {
249
    plan tests => 2;
250
251
    my $item = build_a_test_item();
252
    my $patron = build_a_test_patron();
253
    issue_item($item, $patron);
254
    $item = Koha::Items->find($item->itemnumber);
255
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
256
    my $expecting = 'Koha::Exceptions::Item::CheckedOut';
257
    ok($item->onloan, 'When I look at the item, I see that it is '
258
       .'on loan.');
259
    is(ref($itemcalc->onloan), $expecting, "When I check availability "
260
       ."calculation for item onloan, then exception $expecting is given.");
261
};
262
263
subtest 'restricted' => \&t_restricted;
264
sub t_restricted {
265
    plan tests => 2;
266
267
    my $item = build_a_test_item()->set({restricted=>1})->store;
268
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
269
    my $expecting = 'Koha::Exceptions::Item::Restricted';
270
271
    is($item->restricted, 1, 'When I look at the item, I see that it is '
272
       .'restricted.');
273
    is(ref($itemcalc->restricted), $expecting, "When I check availability "
274
       ."calculation for item restricted, then exception $expecting is given.");
275
};
276
277
subtest 'transfer' => \&t_transfer;
278
sub t_transfer {
279
    plan tests => 5;
280
281
    my $item = build_a_test_item();
282
    my $item2 = build_a_test_item();
283
    my $transfer = Koha::Item::Transfer->new({
284
        itemnumber => $item->itemnumber,
285
        datesent => '2000-12-12 12:12:12',
286
        frombranch => $item->homebranch,
287
        tobranch => $item2->homebranch,
288
        comments => 'Very transfer',
289
    })->store;
290
291
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
292
    my $expecting = 'Koha::Exceptions::Item::Transfer';
293
    ok($transfer, 'Item is in transfer.');
294
    is(ref($itemcalc->transfer), $expecting, "When I check availability"
295
       ." calculation for item transfer, then exception $expecting is given.");
296
    $transfer->datearrived('2001-12-12 12:12:12')->store;
297
    is(ref($itemcalc->transfer), '', "But after item has arrived, "
298
       ."no exception is given.");
299
    $itemcalc = Koha::Availability::Checks::Item->new($item2);
300
    ok(!Koha::Item::Transfers->find({ itemnumber => $item2->itemnumber}),
301
       'Another item is not in transfer.');
302
    ok(!$itemcalc->transfer, "When I check availability calculation for another"
303
       ." item transfer, then no exception is given.");
304
};
305
306
subtest 'transfer_limit' => \&t_transfer_limit;
307
sub t_transfer_limit {
308
    plan tests => 8;
309
310
    t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
311
    t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
312
313
    my $item = build_a_test_item();
314
    my $another_branch = Koha::Libraries->find(
315
            $builder->build({ source => 'Branch' })->{'branchcode'});
316
    my $third_branch = Koha::Libraries->find(
317
            $builder->build({ source => 'Branch' })->{'branchcode'});
318
    my $expecting = 'Koha::Exceptions::Item::CannotBeTransferred';
319
320
    is(C4::Circulation::CreateBranchTransferLimit(
321
        $another_branch->branchcode,
322
        $item->holdingbranch,
323
        $item->effective_itemtype
324
    ), 1, 'There is a branch transfer limit for itemtype from '
325
       .$item->holdingbranch.' to '.$another_branch->branchcode .'.');
326
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
327
328
    is(ref($itemcalc->transfer_limit($another_branch->branchcode)), $expecting,
329
       "When I check availability calculation for transfer limit, then $expecting is given.");
330
    ok(!$itemcalc->transfer_limit($third_branch->branchcode),
331
       'However, item can be transferred from'
332
       .' a library to another library when there are no branch transfer limits.');
333
334
    t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'ccode');
335
    ok(!$itemcalc->transfer_limit($another_branch->branchcode),
336
       'When we change limit to ccode, previous'
337
       .' transfer limit is not in action anymore.');
338
    is(C4::Circulation::CreateBranchTransferLimit(
339
        $another_branch->branchcode,
340
        $item->holdingbranch,
341
        $item->ccode
342
    ), 1, 'Then we added a branch transfer limit by ccode.');
343
    is(ref($itemcalc->transfer_limit($another_branch->branchcode)), $expecting,
344
       "When I check availability calculation for for transfer limit,"
345
       ." then $expecting is given.");
346
347
    t::lib::Mocks::mock_preference('UseBranchTransferLimits', 0);
348
    is(C4::Context->preference('UseBranchTransferLimits'), 0,
349
       'Then, we switched UseBranchTransferLimits off.');
350
    ok(!$itemcalc->transfer_limit($another_branch->branchcode),
351
       'Then no exception is given anymore.');
352
}
353
354
subtest 'unknown_barcode' => \&t_unknown_barcode;
355
sub t_unknown_barcode {
356
    plan tests => 2;
357
358
    my $item = build_a_test_item()->set({barcode=>undef})->store;
359
    my $expecting = 'Koha::Exceptions::Item::UnknownBarcode';
360
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
361
362
    is($item->barcode, undef, 'When I look at the item, we see that it has '
363
       .'undefined barcode.');
364
    is(ref($itemcalc->unknown_barcode), $expecting,
365
       "When I check availability calculation for for unknown barcode, "
366
       ."then $expecting is given.");
367
};
368
369
subtest 'withdrawn' => \&t_withdrawn;
370
sub t_withdrawn {
371
    plan tests => 2;
372
373
    my $item = build_a_test_item()->set({withdrawn=>1})->store;
374
    my $itemcalc = Koha::Availability::Checks::Item->new($item);
375
    my $expecting = 'Koha::Exceptions::Item::Withdrawn';
376
377
    is($item->withdrawn, 1, 'When I look at the item, I see that it is '
378
       .'withdrawn.');
379
    is(ref($itemcalc->withdrawn), $expecting, "When I check availability"
380
       ." calculation for item withdrawn, then exception $expecting is given.");
381
};
382
383
$schema->storage->txn_rollback;
384
385
1;
(-)a/t/db_dependent/Koha/Availability/Checks/LibraryItemRule.t (+312 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 => 3;
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::Items;
28
29
use Koha::Availability::Checks::LibraryItemRule;
30
31
my $schema = Koha::Database->new->schema;
32
$schema->storage->txn_begin;
33
34
my $dbh = C4::Context->dbh;
35
$dbh->{RaiseError} = 1;
36
37
my $builder = t::lib::TestBuilder->new;
38
39
subtest 'Given CircControl is configured as ItemHomeLibrary' => sub {
40
    plan tests => 3;
41
42
    subtest 'hold_not_allowed_by_library, item home library' => \&t_hold_not_allowed_item_home_library_item;
43
    sub t_hold_not_allowed_item_home_library_item {
44
        plan tests => 3;
45
46
        set_default_system_preferences();
47
        set_default_circulation_rules();
48
        t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
49
50
        my $item = build_a_test_item();
51
        ok($dbh->do(q{
52
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
53
            VALUES (?, ?, ?, ?)
54
        }, {}, $item->homebranch, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
55
           .' rule that says holds are not allowed from item home library.');
56
57
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
58
            item => $item
59
        });
60
        my $expecting = 'Koha::Exceptions::Hold::NotAllowedByLibrary';
61
62
        is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'CircControl system preference'
63
           .' is configured as ItemHomeLibrary.');
64
        is(ref($libitemcalc->hold_not_allowed_by_library), $expecting,
65
           "When I check if hold is allowed by library item rules, exception $expecting is given.");
66
    };
67
68
    subtest 'hold_not_allowed_by_library, patron library' => \&t_hold_not_allowed_item_home_library_patron;
69
    sub t_hold_not_allowed_item_home_library_patron  {
70
        plan tests => 3;
71
72
        set_default_system_preferences();
73
        set_default_circulation_rules();
74
        t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
75
76
        my $item = build_a_test_item();
77
        my $patron = build_a_test_patron();
78
        ok($dbh->do(q{
79
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
80
            VALUES (?, ?, ?, ?)
81
        }, {}, $patron->branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
82
           .' rule that says holds are not allowed from patron library.');
83
84
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
85
            item => $item,
86
            patron => $patron,
87
        });
88
89
        is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'CircControl system preference'
90
           .' is configured as ItemHomeLibrary.');
91
        ok(!$libitemcalc->hold_not_allowed_by_library,
92
           'When I check if hold is allowed by library item rules, then no exception is given.');
93
    };
94
95
    subtest 'hold_not_allowed_by_library, logged in library' => \&t_hold_not_allowed_item_home_library_logged_in;
96
    sub t_hold_not_allowed_item_home_library_logged_in {
97
        plan tests => 3;
98
99
        set_default_system_preferences();
100
        set_default_circulation_rules();
101
        t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
102
103
        my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
104
        C4::Context->_new_userenv('xxx');
105
        C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
106
        my $item = build_a_test_item();
107
        my $patron = build_a_test_patron();
108
        ok($dbh->do(q{
109
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
110
            VALUES (?, ?, ?, ?)
111
        }, {}, $branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
112
           .' rule that says holds are not allowed from logged in library.');
113
114
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
115
            item => $item,
116
            patron => $patron
117
        });
118
119
        is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'CircControl system preference'
120
           .' is configured as ItemHomeLibrary.');
121
        ok(!$libitemcalc->hold_not_allowed_by_library,
122
           'When I check if hold is allowed by library item rules, then no exception is given.');
123
    };
124
};
125
126
subtest 'Given CircControl is configured as PatronLibrary' => sub {
127
    plan tests => 3;
128
129
    subtest 'hold_not_allowed_by_library, item home library' => \&t_hold_not_allowed_patron_library_item;
130
    sub t_hold_not_allowed_patron_library_item {
131
        plan tests => 3;
132
133
        set_default_system_preferences();
134
        set_default_circulation_rules();
135
        t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
136
137
        my $item = build_a_test_item();
138
        my $patron = build_a_test_patron();
139
        ok($dbh->do(q{
140
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
141
            VALUES (?, ?, ?, ?)
142
        }, {}, $item->homebranch, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
143
           .' rule that says holds are not allowed from item home library.');
144
145
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
146
            item => $item,
147
            patron => $patron
148
        });
149
150
        is(C4::Context->preference('CircControl'), 'PatronLibrary', 'CircControl system preference'
151
           .' is configured as PatronLibrary.');
152
        ok(!$libitemcalc->hold_not_allowed_by_library,
153
           'When I check if hold is allowed by library item rules, then no exception is given.');
154
    };
155
156
    subtest 'hold_not_allowed_by_library, patron library' => \&t_hold_not_allowed_patron_library_patron;
157
    sub t_hold_not_allowed_patron_library_patron {
158
        plan tests => 3;
159
160
        set_default_system_preferences();
161
        set_default_circulation_rules();
162
        t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
163
164
        my $item = build_a_test_item();
165
        my $patron = build_a_test_patron();
166
        ok($dbh->do(q{
167
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
168
            VALUES (?, ?, ?, ?)
169
        }, {}, $patron->branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
170
           .' rule that says holds are not allowed from patron library.');
171
172
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
173
            item => $item,
174
            patron => $patron
175
        });
176
        my $expecting = 'Koha::Exceptions::Hold::NotAllowedByLibrary';
177
178
        is(C4::Context->preference('CircControl'), 'PatronLibrary', 'CircControl system preference'
179
           .' is configured as PatronLibrary.');
180
        is(ref($libitemcalc->hold_not_allowed_by_library), $expecting,
181
           "When I check if hold is allowed by library item rules, exception $expecting is given.");
182
    };
183
184
    subtest 'hold_not_allowed_by_library, logged in library' => \&t_hold_not_allowed_patron_library_logged_in;
185
    sub t_hold_not_allowed_patron_library_logged_in {
186
        plan tests => 3;
187
188
        set_default_system_preferences();
189
        set_default_circulation_rules();
190
        t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
191
192
        my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
193
        C4::Context->_new_userenv('xxx');
194
        C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
195
        my $item = build_a_test_item();
196
        my $patron = build_a_test_patron();
197
        ok($dbh->do(q{
198
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
199
            VALUES (?, ?, ?, ?)
200
        }, {}, $branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
201
           .' rule that says holds are not allowed from logged in library.');
202
203
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
204
            item => $item,
205
            patron => $patron
206
        });
207
208
        is(C4::Context->preference('CircControl'), 'PatronLibrary', 'CircControl system preference'
209
           .' is configured as PatronLibrary.');
210
        ok(!$libitemcalc->hold_not_allowed_by_library,
211
           'When I check if hold is allowed by library item rules, then no exception is given.');
212
    };
213
};
214
215
subtest 'Given CircControl is configured as PickupLibrary' => sub {
216
    plan tests => 3;
217
218
    subtest 'hold_not_allowed_by_library, item home library' => \&t_hold_not_allowed_pickup_library_item;
219
    sub t_hold_not_allowed_pickup_library_item {
220
        plan tests => 3;
221
222
        set_default_system_preferences();
223
        set_default_circulation_rules();
224
        t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
225
226
        my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
227
        C4::Context->_new_userenv('xxx');
228
        C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
229
        my $item = build_a_test_item();
230
        my $patron = build_a_test_patron();
231
        ok($dbh->do(q{
232
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
233
            VALUES (?, ?, ?, ?)
234
        }, {}, $item->homebranch, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
235
           .' rule that says holds are not allowed from item home library.');
236
237
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
238
            item => $item,
239
            patron => $patron
240
        });
241
242
        is(C4::Context->preference('CircControl'), 'PickupLibrary', 'CircControl system preference'
243
           .' is configured as PickupLibrary.');
244
        ok(!$libitemcalc->hold_not_allowed_by_library,
245
           'When I check if hold is allowed by library item rules, then no exception is given.');
246
    };
247
248
    subtest 'hold_not_allowed_by_library, patron library' => \&t_hold_not_allowed_pickup_library_patron;
249
    sub t_hold_not_allowed_pickup_library_patron  {
250
        plan tests => 3;
251
252
        set_default_system_preferences();
253
        set_default_circulation_rules();
254
        t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
255
256
        my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
257
        C4::Context->_new_userenv('xxx');
258
        C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
259
        my $item = build_a_test_item();
260
        my $patron = build_a_test_patron();
261
        ok($dbh->do(q{
262
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
263
            VALUES (?, ?, ?, ?)
264
        }, {}, $patron->branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
265
           .' rule that says holds are not allowed from patron library.');
266
267
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
268
            item => $item,
269
            patron => $patron,
270
        });
271
272
        is(C4::Context->preference('CircControl'), 'PickupLibrary', 'CircControl system preference'
273
           .' is configured as PickupLibrary.');
274
        ok(!$libitemcalc->hold_not_allowed_by_library,
275
           'When I check if hold is allowed by library item rules, then no exception is given.');
276
    };
277
    subtest 'hold_not_allowed_by_library for patron library, CircControl = PickupLibrary' => \&t_hold_not_allowed_pickup_library_logged_in;
278
    sub t_hold_not_allowed_pickup_library_logged_in {
279
        plan tests => 3;
280
281
        set_default_system_preferences();
282
        set_default_circulation_rules();
283
        t::lib::Mocks::mock_preference('CircControl', 'PickupLibrary');
284
285
        my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
286
        C4::Context->_new_userenv('xxx');
287
        C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
288
289
        my $item = build_a_test_item();
290
        my $patron = build_a_test_patron();
291
        ok($dbh->do(q{
292
            INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch)
293
            VALUES (?, ?, ?, ?)
294
        }, {}, $branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item'
295
           .' rule that says holds are not allowed from logged in library.');
296
297
        my $libitemcalc = Koha::Availability::Checks::LibraryItemRule->new({
298
            item => $item,
299
            patron => $patron,
300
        });
301
        my $expecting = 'Koha::Exceptions::Hold::NotAllowedByLibrary';
302
303
        is(C4::Context->preference('CircControl'), 'PickupLibrary', 'CircControl system preference'
304
           .' is configured as PickupLibrary.');
305
        is(ref($libitemcalc->hold_not_allowed_by_library), $expecting,
306
           "When I check if hold is allowed by library item rules, exception $expecting is given.");
307
    };
308
};
309
310
$schema->storage->txn_rollback;
311
312
1;
(-)a/t/db_dependent/Koha/Availability/Checks/Patron.t (+452 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 => 20;
22
use t::lib::Mocks;
23
use t::lib::TestBuilder;
24
require t::db_dependent::Koha::Availability::Helpers;
25
26
use C4::Members;
27
28
use Koha::Account::Lines;
29
use Koha::Biblioitems;
30
use Koha::Database;
31
use Koha::DateUtils;
32
use Koha::Items;
33
34
use Koha::Availability::Checks::Patron;
35
36
my $schema = Koha::Database->new->schema;
37
$schema->storage->txn_begin;
38
39
my $dbh = C4::Context->dbh;
40
$dbh->{RaiseError} = 1;
41
42
my $builder = t::lib::TestBuilder->new;
43
44
set_default_system_preferences();
45
set_default_circulation_rules();
46
47
subtest 'debarred, given patron is not debarred' => \&t_not_debarred;
48
sub t_not_debarred {
49
    plan tests => 3;
50
51
    my $patron = build_a_test_patron();
52
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
53
    my $expecting = 'Koha::Exceptions::Patron::Debarred';
54
55
    is($patron->debarred, undef, 'Patron is not debarred.');
56
    is($patron->debarredcomment, undef, 'Patron does not have debarment comment.');
57
    ok(!$patroncalc->debarred, 'When I check patron debarment, then no exception is given.');
58
};
59
60
subtest 'debarred, given patron is debarred' => \&t_debarred;
61
sub t_debarred {
62
    plan tests => 5;
63
64
    my $patron = build_a_test_patron();
65
    my $hundred_days = output_pref({ dt => dt_from_string()->add_duration(
66
                          DateTime::Duration->new(days => 100)), dateformat => 'iso', dateonly => 1 });
67
    $patron->set({ debarred => $hundred_days, debarredcomment => 'DONT EVER COME BACK' })->store;
68
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
69
    my $expecting = 'Koha::Exceptions::Patron::Debarred';
70
    my $debarred = $patroncalc->debarred;
71
72
    is($patron->debarred, $hundred_days, "Patron is debarred until $hundred_days.");
73
    is($patron->debarredcomment, 'DONT EVER COME BACK', 'Library does not want them to ever come back.');
74
    is(ref($debarred), $expecting, "When I check patron debarment, then $expecting is given.");
75
    is($debarred->expiration, $patron->debarred, 'Then, from the status, I can see the expiration date.');
76
    is($debarred->comment, $patron->debarredcomment, 'Then, from the status, I can see that patron should never come back.');
77
};
78
79
subtest 'debt_checkout, given patron has less than noissuescharge' => \&t_fines_checkout_less_than_max;
80
sub t_fines_checkout_less_than_max {
81
    plan tests => 8;
82
83
    t::lib::Mocks::mock_preference('noissuescharge', 9001);
84
    t::lib::Mocks::mock_preference('AllFinesNeedOverride', 0);
85
86
    my $patron = build_a_test_patron();
87
    my $line = Koha::Account::Line->new({
88
        borrowernumber => $patron->borrowernumber,
89
        amountoutstanding => 9000,
90
    })->store;
91
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
92
    my $maxoutstanding = C4::Context->preference('noissuescharge');
93
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
94
    my $debt = $patroncalc->debt_checkout;
95
96
    is(C4::Context->preference('AllFinesNeedOverride'), 0, 'Not all fines need override.');
97
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
98
    ok($maxoutstanding > $outstanding, 'When I check patron\'s balance, I found out they have less outstanding fines than maximum allowed.');
99
    ok(!$debt, 'When I check patron debt, then no exception is given.');
100
    t::lib::Mocks::mock_preference('AllFinesNeedOverride', 1);
101
    is(C4::Context->preference('AllFinesNeedOverride'), 1, 'Given we configure'
102
       .' all fines needing override.');
103
    $debt = $patroncalc->debt_checkout;
104
    my $expecting = 'Koha::Exceptions::Patron::Debt';
105
    is(ref($debt), $expecting, "When I check patron debt, then $expecting is given.");
106
    is($debt->max_outstanding, $maxoutstanding, 'Then I can see the status showing me how much'
107
       .' outstanding total can be at maximum.');
108
    is($debt->current_outstanding, $outstanding, 'Then I can see the status showing me how much'
109
       .' outstanding fines patron has right now.');
110
};
111
112
subtest 'debt_checkout, given patron has more than noissuescharge' => \&t_fines_checkout_more_than_max;
113
sub t_fines_checkout_more_than_max {
114
    plan tests => 5;
115
116
    t::lib::Mocks::mock_preference('noissuescharge', 5);
117
118
    my $patron = build_a_test_patron();
119
    my $line = Koha::Account::Line->new({
120
        borrowernumber => $patron->borrowernumber,
121
        amountoutstanding => 9001,
122
    })->store;
123
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
124
    my $maxoutstanding = C4::Context->preference('noissuescharge');
125
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
126
    my $debt = $patroncalc->debt_checkout;
127
    my $expecting = 'Koha::Exceptions::Patron::Debt';
128
129
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
130
    ok($maxoutstanding < $outstanding, 'When I check patron\'s balance, I found out they have more outstanding fines than allowed.');
131
    is(ref($debt), $expecting, "When I check patron debt, then $expecting is given.");
132
    is($debt->max_outstanding, $maxoutstanding, 'Then I can see the status showing me how much'
133
       .' outstanding total can be at maximum.');
134
    is($debt->current_outstanding, $outstanding, 'Then I can see the status showing me how much'
135
       .' outstanding fines patron has right now.');
136
};
137
138
subtest 'debt_hold, given patron has less than maxoutstanding' => \&t_fines_hold_less_than_max;
139
sub t_fines_hold_less_than_max {
140
    plan tests => 3;
141
142
    t::lib::Mocks::mock_preference('maxoutstanding', 9001);
143
144
    my $patron = build_a_test_patron();
145
    my $line = Koha::Account::Line->new({
146
        borrowernumber => $patron->borrowernumber,
147
        amountoutstanding => 9000,
148
    })->store;
149
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
150
    my $maxoutstanding = C4::Context->preference('maxoutstanding');
151
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
152
    my $debt = $patroncalc->debt_hold;
153
154
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
155
    ok($maxoutstanding > $outstanding, 'When I check patron\'s balance, I found out they have less outstanding fines than maximum allowed.');
156
    ok(!$debt, 'When I check patron debt, then no exception is given.');
157
};
158
159
subtest 'debt_hold, given patron has more than maxoutstanding' => \&t_fines_hold_more_than_max;
160
sub t_fines_hold_more_than_max {
161
    plan tests => 5;
162
163
    t::lib::Mocks::mock_preference('maxoutstanding', 5);
164
165
    my $patron = build_a_test_patron();
166
    my $line = Koha::Account::Line->new({
167
        borrowernumber => $patron->borrowernumber,
168
        amountoutstanding => 9001,
169
    })->store;
170
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
171
    my $maxoutstanding = C4::Context->preference('maxoutstanding');
172
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
173
    my $debt = $patroncalc->debt_hold;
174
    my $expecting = 'Koha::Exceptions::Patron::Debt';
175
176
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
177
    ok($maxoutstanding < $outstanding, 'When I check patron\'s balance, I found out they have more outstanding fines than allowed.');
178
    is(ref($debt), $expecting, "When I check patron debt, then $expecting is given.");
179
    is($debt->max_outstanding, $maxoutstanding, 'Then I can see the status showing me how much'
180
       .' outstanding total can be at maximum.');
181
    is($debt->current_outstanding, $outstanding, 'Then I can see the status showing me how much'
182
       .' outstanding fines patron has right now.');
183
};
184
185
subtest 'debt_renew_opac, given patron has less than OPACFineNoRenewals' => \&t_fines_renew_opac_less_than_max;
186
sub t_fines_renew_opac_less_than_max {
187
    plan tests => 3;
188
189
    t::lib::Mocks::mock_preference('OPACFineNoRenewals', 9001);
190
191
    my $patron = build_a_test_patron();
192
    my $line = Koha::Account::Line->new({
193
        borrowernumber => $patron->borrowernumber,
194
        amountoutstanding => 9000,
195
    })->store;
196
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
197
    my $maxoutstanding = C4::Context->preference('OPACFineNoRenewals');
198
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
199
    my $debt = $patroncalc->debt_renew_opac;
200
201
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
202
    ok($maxoutstanding > $outstanding, 'When I check patron\'s balance, I found out they have less outstanding fines than maximum allowed.');
203
    ok(!$debt, 'When I check patron debt, then no exception is given.');
204
};
205
206
subtest 'debt_renew_opac, given patron has more than OPACFineNoRenewals' => \&t_fines_renew_opac_more_than_max;
207
sub t_fines_renew_opac_more_than_max {
208
    plan tests => 5;
209
210
    t::lib::Mocks::mock_preference('OPACFineNoRenewals', 5);
211
212
    my $patron = build_a_test_patron();
213
    my $line = Koha::Account::Line->new({
214
        borrowernumber => $patron->borrowernumber,
215
        amountoutstanding => 9001,
216
    })->store;
217
    my ($outstanding) = C4::Members::GetMemberAccountRecords($patron->borrowernumber);
218
    my $maxoutstanding = C4::Context->preference('OPACFineNoRenewals');
219
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
220
    my $debt = $patroncalc->debt_renew_opac;
221
    my $expecting = 'Koha::Exceptions::Patron::Debt';
222
223
    ok($maxoutstanding, 'When I look at system preferences, I see that maximum allowed outstanding fines is set.');
224
    ok($maxoutstanding < $outstanding, 'When I check patron\'s balance, I found out they have more outstanding fines than allowed.');
225
    is(ref($debt), $expecting, "When I check patron debt, then $expecting is given.");
226
    is($debt->max_outstanding, $maxoutstanding, 'Then I can see the status showing me how much'
227
       .' outstanding total can be at maximum.');
228
    is($debt->current_outstanding, $outstanding, 'Then I can see the status showing me how much'
229
       .' outstanding fines patron has right now.');
230
};
231
232
subtest 'debt_checkout_guarantees, given patron\'s guarantees have less than NoIssuesChargeGuarantees' => \&t_guarantees_fines_checkout_less_than_max;
233
sub t_guarantees_fines_checkout_less_than_max {
234
    plan tests => 3;
235
236
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantees', 5);
237
238
    my $patron = build_a_test_patron();
239
    my $guarantee1 = build_a_test_patron();
240
    $guarantee1->guarantorid($patron->borrowernumber)->store;
241
    my $guarantee2 = build_a_test_patron();
242
    $guarantee2->guarantorid($patron->borrowernumber)->store;
243
    my $line1 = Koha::Account::Line->new({
244
        borrowernumber => $guarantee1->borrowernumber,
245
        amountoutstanding => 2,
246
    })->store;
247
    my $line2 = Koha::Account::Line->new({
248
        borrowernumber => $guarantee2->borrowernumber,
249
        amountoutstanding => 2,
250
    })->store;
251
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
252
    my $debt = $patroncalc->debt_checkout_guarantees;
253
254
    ok($line1, 'We have added very small fines to patron\'s guarantees.');
255
    ok($line1->amountoutstanding+$line2->amountoutstanding < C4::Context->preference('NoIssuesChargeGuarantees'),
256
       'These fines total is less than NoIssuesChargeGuarantees');
257
    ok(!$debt,  'When I check patron guarantees debt, then no exception is given.');
258
};
259
260
subtest 'debt_checkout_guarantees, given patron\s guarantees have more than NoIssuesChargeGuarantees' => \&t_guarantees_fines_checkout_more_than_max;
261
sub t_guarantees_fines_checkout_more_than_max {
262
    plan tests => 5;
263
264
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantees', 5);
265
266
    my $patron = build_a_test_patron();
267
    my $guarantee1 = build_a_test_patron();
268
    $guarantee1->guarantorid($patron->borrowernumber)->store;
269
    my $guarantee2 = build_a_test_patron();
270
    $guarantee2->guarantorid($patron->borrowernumber)->store;
271
    my $line1 = Koha::Account::Line->new({
272
        borrowernumber => $guarantee1->borrowernumber,
273
        amountoutstanding => 3,
274
    })->store;
275
    my $line2 = Koha::Account::Line->new({
276
        borrowernumber => $guarantee2->borrowernumber,
277
        amountoutstanding => 3,
278
    })->store;
279
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
280
    my $debt = $patroncalc->debt_checkout_guarantees;
281
    my $expecting = 'Koha::Exceptions::Patron::DebtGuarantees';
282
283
    ok($line1, 'We have added very small fines to patron\'s guarantees.');
284
    ok($line1->amountoutstanding+$line2->amountoutstanding > C4::Context->preference('NoIssuesChargeGuarantees'),
285
       'These fines total is more than NoIssuesChargeGuarantees');
286
    is(ref($debt), $expecting, "When I check patron guarantees debt, then $expecting is given.");
287
    is($debt->max_outstanding, 5, 'Then I can see the status showing me how much'
288
       .' outstanding total can be at maximum.');
289
    is($debt->current_outstanding, 6, 'Then I can see the status showing me how much'
290
       .' outstanding fines patron guarantees have right now.');
291
};
292
293
subtest 'exceeded_maxreserves, given patron not exceeding max reserves' => \&t_exceeded_maxreserves_not;
294
sub t_exceeded_maxreserves_not {
295
    plan tests => 2;
296
297
    my $patron = build_a_test_patron();
298
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
299
    my $holds = Koha::Holds->search({ borrowernumber => $patron->borrowernumber });
300
301
    is($holds->count, 0, 'Patron has no holds.');
302
    ok(!$patroncalc->exceeded_maxreserves, 'When I check if patron exceeded maxreserves, then no exception is given.');
303
};
304
305
subtest 'exceeded_maxreserves, given patron exceeding max reserves' => \&t_exceeded_maxreserves;
306
sub t_exceeded_maxreserves {
307
    plan tests => 5;
308
309
    t::lib::Mocks::mock_preference('maxreserves', 1);
310
311
    my $item = build_a_test_item();
312
    my $item2 = build_a_test_item();
313
    my $patron = build_a_test_patron();
314
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
315
    add_biblio_level_hold($item, $patron, $patron->branchcode);
316
    add_biblio_level_hold($item2, $patron, $patron->branchcode);
317
    my $holds = Koha::Holds->search({ borrowernumber => $patron->borrowernumber });
318
    my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached';
319
    my $exceeded = $patroncalc->exceeded_maxreserves;
320
321
    is(C4::Context->preference('maxreserves'), 1, 'Maximum number of holds allowed is one.');
322
    is($holds->count, 2, 'Patron has two holds.');
323
    is(ref($exceeded), $expecting, "When I check patron expiration, then $expecting is given.");
324
    is($exceeded->max_holds_allowed, 1, 'Then I can see the status showing me how many'
325
       .' holds are allowed at max.');
326
    is($exceeded->current_hold_count, 2, 'Then I can see the status showing me how many'
327
       .' holds patron has right now.');
328
};
329
330
subtest 'expired, given patron is not expired' => \&t_not_expired;
331
sub t_not_expired {
332
    plan tests => 2;
333
334
    my $patron = build_a_test_patron();
335
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
336
    my $dt_expiry = dt_from_string($patron->dateexpiry);
337
    my $now = DateTime->now(time_zone => C4::Context->tz);
338
339
    is(DateTime->compare($dt_expiry, $now), 1, 'Patron is not expired.');
340
    ok(!$patroncalc->expired, 'When I check patron expiration, then no exception is given.');
341
};
342
343
subtest 'expired, given patron is expired' => \&t_expired;
344
sub t_expired {
345
    plan tests => 2;
346
347
    my $patron = build_a_test_patron();
348
    $patron->dateexpiry('1999-10-10')->store;
349
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
350
    my $dt_expiry = dt_from_string($patron->dateexpiry);
351
    my $now = DateTime->now(time_zone => C4::Context->tz);
352
    my $expecting = 'Koha::Exceptions::Patron::CardExpired';
353
354
    is(DateTime->compare($now, $dt_expiry), 1, 'Patron finds out their card is expired.');
355
    is(ref($patroncalc->expired), $expecting, "When I check patron expiration, then $expecting is given.");
356
};
357
358
subtest 'gonenoaddress, patron not gonenoaddress' => \&t_not_gonenoaddress;
359
sub t_not_gonenoaddress {
360
    plan tests => 2;
361
362
    my $patron = build_a_test_patron();
363
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
364
365
    ok(!$patron->gonenoaddress, 'Patron is not gonenoaddress.');
366
    ok(!$patroncalc->gonenoaddress, 'When I check patron gonenoaddress, then no exception is given.');
367
};
368
369
subtest 'gonenoaddress, patron gonenoaddress' => \&t_gonenoaddress;
370
sub t_gonenoaddress {
371
    plan tests => 2;
372
373
    my $patron = build_a_test_patron();
374
    $patron->gonenoaddress('1')->store;
375
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
376
    my $expecting = 'Koha::Exceptions::Patron::GoneNoAddress';
377
378
    is($patron->gonenoaddress, 1, 'Patron is gonenoaddress.');
379
    is(ref($patroncalc->gonenoaddress), $expecting, "When I check patron gonenoaddress, then $expecting is given.");
380
};
381
382
subtest 'lost, patron card not lost' => \&t_not_lost;
383
sub t_not_lost {
384
    plan tests => 2;
385
386
    my $patron = build_a_test_patron();
387
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
388
389
    ok(!$patron->lost, 'Patron card is not lost.');
390
    ok(!$patroncalc->lost, 'When I check if patron card is lost, then no exception is given.');
391
};
392
393
subtest 'lost, patron card lost' => \&t_lost;
394
sub t_lost {
395
    plan tests => 2;
396
397
    my $patron = build_a_test_patron();
398
    $patron->lost('1')->store;
399
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
400
    my $expecting = 'Koha::Exceptions::Patron::CardLost';
401
402
    is($patron->lost, 1, 'Patron card is lost.');
403
    is(ref($patroncalc->lost), $expecting, "When I check if patron card is lost, then $expecting is given.");
404
};
405
406
subtest 'from_another_library, patron from same branch than me' => \&t_from_another_library_same_branch;
407
sub t_from_another_library_same_branch {
408
    plan tests => 2;
409
410
    t::lib::Mocks::mock_preference('IndependentBranches', 0);
411
412
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
413
414
    C4::Context->_new_userenv('xxx');
415
    C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', '', '', '');
416
417
    my $patron = build_a_test_patron();
418
    $patron->branchcode($branchcode)->store;
419
420
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
421
422
    is(C4::Context->userenv->{branch}, $patron->branchcode, 'Patron is from same branch as me.');
423
    ok(!$patroncalc->from_another_library, 'When I check if patron is'
424
       .' from same branch as me, then no exception is given.');
425
}
426
427
subtest 'from_another_library, patron from different branch than me' => \&t_from_another_library;
428
sub t_from_another_library {
429
    plan tests => 2;
430
431
    t::lib::Mocks::mock_preference('IndependentBranches', 1);
432
433
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
434
    my $branchcode2 = $builder->build({ source => 'Branch' })->{'branchcode'};
435
436
    C4::Context->_new_userenv('xxx');
437
    C4::Context->set_userenv(0,0,0,'firstname','surname', $branchcode, 'Midway Public Library', undef, '', '');
438
439
    my $patron = build_a_test_patron();
440
    $patron->branchcode($branchcode2)->store;
441
442
    my $patroncalc = Koha::Availability::Checks::Patron->new($patron);
443
    my $expecting = 'Koha::Exceptions::Patron::FromAnotherLibrary';
444
445
    isnt(C4::Context->userenv->{branch}, $patron->branchcode, 'Patron is not from same branch as me.');
446
    is(ref($patroncalc->from_another_library), $expecting, "When I check if patron is"
447
       ." from same branch as me, then $expecting is given.");
448
}
449
450
$schema->storage->txn_rollback;
451
452
1;
(-)a/t/db_dependent/Koha/Availability/Helpers.pm (-1 / +165 lines)
Line 0 Link Here
0
- 
1
use t::lib::Mocks;
2
use t::lib::TestBuilder;
3
4
use C4::Circulation;
5
use C4::Context;
6
use C4::Reserves;
7
8
use Koha::Biblios;
9
use Koha::Biblioitems;
10
use Koha::DateUtils;
11
use Koha::IssuingRules;
12
use Koha::Items;
13
use Koha::Patrons;
14
15
sub build_a_test_item {
16
    my ($biblio, $biblioitem) = @_;
17
18
    my $builder = t::lib::TestBuilder->new;
19
    my $branch_built = $builder->build({source => 'Branch'});
20
    my $itemtype_built = $builder->build({
21
        source => 'Itemtype',
22
        value => {
23
            notforloan => 0,
24
            rentalcharge => 0,
25
        }
26
    });
27
    my $another_itemtype_built = $builder->build({
28
        source => 'Itemtype',
29
        value => {
30
            notforloan => 0,
31
            rentalcharge => 0,
32
        }
33
    });
34
    my $bib = MARC::Record->new();
35
    my $title = 'Silence in the library';
36
    if( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
37
        $bib->append_fields(
38
            MARC::Field->new('600', '', '1', a => 'Moffat, Steven'),
39
            MARC::Field->new('200', '', '', a => $title),
40
        );
41
    }
42
    else {
43
        $bib->append_fields(
44
            MARC::Field->new('100', '', '', a => 'Moffat, Steven'),
45
            MARC::Field->new('245', '', '', a => $title),
46
        );
47
    }
48
    my ($bibnum, $bibitemnum) = C4::Biblio::AddBiblio($bib, '');
49
    $biblio ||= Koha::Biblios->find($bibnum);
50
    $biblioitem ||= Koha::Biblioitems->find($bibitemnum);
51
    $biblioitem->itemtype($another_itemtype_built->{'itemtype'})->store;
52
    my $item = Koha::Items->find($builder->build({
53
        source => 'Item',
54
        value => {
55
            notforloan => 0,
56
            damaged => 0,
57
            biblioitemnumber => $biblioitem->biblioitemnumber,
58
            biblionumber => $biblio->biblionumber,
59
            itype => $itemtype_built->{'itemtype'},
60
            itemlost => 0,
61
            withdrawn => 0,
62
            onloan => undef,
63
            restricted => 0,
64
            homebranch => $branch_built->{'branchcode'},
65
            holdingbranch => $branch_built->{'branchcode'},
66
        }
67
    })->{'itemnumber'});
68
69
    return $item;
70
}
71
72
sub build_a_test_patron {
73
    my ($params) = @_;
74
75
    my $builder = t::lib::TestBuilder->new;
76
    my $branch_built = $builder->build({ source => 'Branch' });
77
    my $cat_built = $builder->build({ source => 'Category' });
78
    return Koha::Patrons->find($builder->build({
79
        source => 'Borrower',
80
        value => {
81
            categorycode => $cat_built->{'categorycode'},
82
            branchcode => $branch_built->{'branchcode'},
83
            debarred => undef,
84
            debarredcomment => undef,
85
            lost => undef,
86
            gonenoaddress => undef,
87
            dateexpiry => output_pref({ dt => dt_from_string()->add_duration( # expires in 100 days
88
                          DateTime::Duration->new(days => 100)), dateformat => 'iso', dateonly => 1 }),
89
            dateofbirth => '1950-10-10',
90
        }
91
    })->{'borrowernumber'});
92
}
93
94
sub set_default_circulation_rules {
95
    my ($params) = @_;
96
97
    my $dbh = C4::Context->dbh;
98
99
    Koha::IssuingRules->search->delete;
100
    my $rule = Koha::IssuingRule->new({
101
        branchcode   => '*',
102
        itemtype     => '*',
103
        categorycode => '*',
104
        maxissueqty => 3,
105
        renewalsallowed => 1,
106
        holds_per_record => 1,
107
        reservesallowed => 1,
108
        opacitemholds => 'Y',
109
    })->store;
110
    $dbh->do('DELETE FROM branch_item_rules');
111
    $dbh->do('DELETE FROM default_branch_circ_rules');
112
    $dbh->do('DELETE FROM default_branch_item_rules');
113
    $dbh->do('DELETE FROM default_circ_rules');
114
}
115
116
sub set_default_system_preferences {
117
    t::lib::Mocks::mock_preference('item-level_itypes', 1);
118
    t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 1);
119
    t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
120
    t::lib::Mocks::mock_preference('BlockExpiredPatronOpacActions', 0);
121
    t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary');
122
    t::lib::Mocks::mock_preference('canreservefromotherbranches', 1);
123
    t::lib::Mocks::mock_preference('IndependentBranches', 0);
124
    t::lib::Mocks::mock_preference('HomeOrHoldingBranch', 'holdingbranch');
125
    t::lib::Mocks::mock_preference('maxoutstanding', 5);
126
    t::lib::Mocks::mock_preference('AgeRestrictionMarker', 'PEGI');
127
    t::lib::Mocks::mock_preference('maxreserves', 50);
128
    t::lib::Mocks::mock_preference('NoIssuesChargeGuarantees', 5);
129
}
130
131
sub add_item_level_hold {
132
    my ($item, $patron, $branch) = @_;
133
134
    return C4::Reserves::AddReserve(
135
            $branch,
136
            $patron->borrowernumber,
137
            $item->biblionumber, '',
138
            1, undef, undef, undef,
139
            undef, $item->itemnumber,
140
    );
141
}
142
143
sub add_biblio_level_hold {
144
    my ($item, $patron, $branch) = @_;
145
146
    return C4::Reserves::AddReserve(
147
            $branch,
148
            $patron->borrowernumber,
149
            $item->biblionumber, '',
150
            1,
151
    );
152
}
153
154
sub issue_item {
155
    my ($item, $patron) = @_;
156
157
    C4::Context->_new_userenv('xxx');
158
    C4::Context->set_userenv(0,0,0,'firstname','surname', $patron->branchcode, 'Midway Public Library', '', '', '');
159
    return C4::Circulation::AddIssue(
160
            $patron->unblessed,
161
            $item->barcode
162
    );
163
}
164
165
1;

Return to bug 17712