Bug 24860: Add ability to select an item group when placing a hold
[koha.git] / Koha / Holds.pm
1 package Koha::Holds;
2
3 # Copyright ByWater Solutions 2014
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
23 use Koha::Database;
24
25 use Koha::Hold;
26
27 use base qw(Koha::Objects);
28
29 =head1 NAME
30
31 Koha::Holds - Koha Hold object set class
32
33 =head1 API
34
35 =head2 Class methods
36
37 =cut
38
39 =head3 waiting
40
41 returns a set of holds that are waiting from an existing set
42
43 =cut
44
45 sub waiting {
46     my ( $self ) = @_;
47
48     return $self->search( { found => 'W' } );
49 }
50
51 =head3 unfilled
52
53 returns a set of holds that are unfilled from an existing set
54
55 =cut
56
57 sub unfilled {
58     my ( $self ) = @_;
59
60     return $self->search( { found => undef } );
61 }
62
63 =head3 forced_hold_level
64
65 If a patron has multiple holds for a single record,
66 those holds must be either all record level holds,
67 or they must all be item level holds.
68
69 This method should be used with Hold sets where all
70 Hold objects share the same patron and record.
71
72 This method will return 'item' if the patron has
73 at least one item level hold. It will return 'record'
74 if the patron has holds but none are item level,
75 Finally, if the patron has no holds, it will return
76 undef which indicates the patron may select either
77 record or item level holds, barring any other rules
78 that would prevent one or the other.
79
80 =cut
81
82 sub forced_hold_level {
83     my ($self) = @_;
84
85     my $item_level_count = $self->search( { itemnumber => { '!=' => undef } } )->count();
86     return 'item' if $item_level_count > 0;
87
88     my $item_group_level_count = $self->search( { item_group_id => { '!=' => undef } } )->count();
89     return 'item_group' if $item_group_level_count > 0;
90
91     my $record_level_count = $self->search( { itemnumber => undef } )->count();
92     return 'record' if $record_level_count > 0;
93
94     return;
95 }
96
97 =head3 get_items_that_can_fill
98
99     my $items = $holds->get_items_that_can_fill();
100
101 Return the list of items that can fill the hold set.
102
103 Items that are not:
104
105   in transit
106   waiting
107   lost
108   widthdrawn
109   not for loan
110   not on loan
111
112 =cut
113
114 sub get_items_that_can_fill {
115     my ( $self ) = @_;
116
117     return Koha::Items->new->empty()
118       unless $self->count() > 0;
119
120     my @itemnumbers = $self->search({ 'me.itemnumber' => { '!=' => undef } })->get_column('itemnumber');
121     my @biblionumbers = $self->search({ 'me.itemnumber' => undef })->get_column('biblionumber');
122     my @bibs_or_items;
123     push @bibs_or_items, 'me.itemnumber' => { in => \@itemnumbers } if @itemnumbers;
124     push @bibs_or_items, 'me.biblionumber' => { in => \@biblionumbers } if @biblionumbers;
125
126     my @branchtransfers = Koha::Item::Transfers->filter_by_current->search({}, {
127             columns  => ['itemnumber'],
128             collapse => 1,
129         }
130     )->get_column('itemnumber');
131     my @waiting_holds = Koha::Holds->search(
132         { 'found' => 'W' },
133         {
134             columns  => ['itemnumber'],
135             collapse => 1,
136         }
137     )->get_column('itemnumber');
138
139     return Koha::Items->search(
140         {
141             -or => \@bibs_or_items,
142             itemnumber   => { -not_in => [ @branchtransfers, @waiting_holds ] },
143             onloan       => undef,
144             notforloan   => 0,
145         }
146     )->filter_by_for_hold();
147 }
148
149 =head3 filter_by_has_cancellation_requests
150
151     my $with_cancellation_reqs = $holds->filter_by_has_cancellation_requests;
152
153 Returns a filtered resultset only containing holds that have cancellation requests.
154
155 =cut
156
157 sub filter_by_has_cancellation_requests {
158     my ($self) = @_;
159
160     return $self->search( { 'hold_cancellation_request_id' => { '!=' => undef } },
161         { join => 'cancellation_requests' } );
162 }
163
164 =head3 filter_out_has_cancellation_requests
165
166     my $holds_without_cancellation_requests = $holds->filter_out_has_cancellation_requests;
167
168 Returns a filtered resultset without holds with cancellation requests.
169
170 =cut
171
172 sub filter_out_has_cancellation_requests {
173     my ($self) = @_;
174
175     return $self->search( { 'hold_cancellation_request_id' => { '=' => undef } },
176         { join => 'cancellation_requests' } );
177 }
178
179 =head2 Internal methods
180
181 =head3 _type
182
183 =cut
184
185 sub _type {
186     return 'Reserve';
187 }
188
189 =head3 object_class
190
191 =cut
192
193 sub object_class {
194     return 'Koha::Hold';
195 }
196
197 =head1 AUTHOR
198
199 Kyle M Hall <kyle@bywatersolutions.com>
200
201 =cut
202
203 1;