Bug 30650: DBIC specific
[koha.git] / Koha / CurbsidePickup.pm
1 package Koha::CurbsidePickup;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Carp;
21
22 use Koha::Database;
23
24 use base qw(Koha::Object);
25
26 use C4::Circulation qw( CanBookBeIssued AddIssue );
27 use C4::Members::Messaging qw( GetMessagingPreferences );
28 use C4::Letters qw( GetPreparedLetter EnqueueLetter );
29 use Koha::Calendar;
30 use Koha::DateUtils qw( dt_from_string );
31 use Koha::Patron;
32 use Koha::Library;
33 use Koha::CurbsidePickupIssues;
34 use Koha::Exceptions::CurbsidePickup;
35
36 =head1 NAME
37
38 Koha::CurbsidePickup - Koha Curbside Pickup Object class
39
40 =head1 API
41
42 =head2 Class methods
43
44 =cut
45
46 =head3 new
47
48 =cut
49
50 sub new {
51     my ( $self, $params ) = @_;
52
53     my $policy =
54       Koha::CurbsidePickupPolicies->find( { branchcode => $params->{branchcode} } );
55
56     Koha::Exceptions::CurbsidePickup::NotEnabled->throw
57       unless $policy && $policy->enabled;
58
59     my $calendar = Koha::Calendar->new( branchcode => $params->{branchcode} );
60     Koha::Exceptions::CurbsidePickup::LibraryIsClosed->throw
61       if $calendar->is_holiday( $params->{scheduled_pickup_datetime} );
62
63     if ( $policy->enable_waiting_holds_only ) {
64         my $patron        = Koha::Patrons->find( $params->{borrowernumber} );
65         my $waiting_holds = $patron->holds->search(
66             { found => 'W', branchcode => $params->{branchcode} } );
67
68         Koha::Exceptions::CurbsidePickup::NoWaitingHolds->throw
69           unless $waiting_holds->count;
70     }
71     my $existing_curbside_pickups = Koha::CurbsidePickups->search(
72         {
73             branchcode                => $params->{branchcode},
74             borrowernumber            => $params->{borrowernumber},
75             delivered_datetime        => undef,
76         }
77     )->filter_by_scheduled_today;
78     Koha::Exceptions::CurbsidePickup::TooManyPickups->throw(
79         branchcode     => $params->{branchcode},
80         borrowernumber => $params->{borrowernumber}
81     ) if $existing_curbside_pickups->count;
82
83     my $is_valid =
84       $policy->is_valid_pickup_datetime( $params->{scheduled_pickup_datetime} );
85     unless ($is_valid) {
86         my $error = @{ $is_valid->messages }[0]->message;
87         Koha::Exceptions::CurbsidePickup::NoMatchingSlots->throw
88           if $error eq 'no_matching_slots';
89         Koha::Exceptions::CurbsidePickup::NoMorePickupsAvailable->throw
90           if $error eq 'no_more_available';
91         Koha::Exceptions->throw(
92             "Error message must raise the appropriate exception");
93     }
94
95     return $self->SUPER::new($params);
96 }
97
98 =head3 notify_new_pickup
99
100 $pickup->notify_new_pickup
101
102 Will notify the patron that the pickup has been created.
103 Letter 'NEW_CURBSIDE_PICKUP will be used', and depending on 'Hold_Filled' configuration.
104
105 =cut
106
107 sub notify_new_pickup {
108     my ( $self ) = @_;
109
110     my $patron = $self->patron;
111
112     my $library = $self->library;
113
114     $patron->queue_notice({ letter_params => {
115         module     => 'reserves',
116         letter_code => 'NEW_CURBSIDE_PICKUP',
117         borrowernumber => $patron->borrowernumber,
118         branchcode => $self->branchcode,
119         tables     => {
120             'branches'  => $library->unblessed,
121             'borrowers' => $patron->unblessed,
122         },
123         substitute => {
124             curbside_pickup => $self,
125         }
126     }, message_name => 'Hold_Filled' });
127 }
128
129 =head3 checkouts
130
131 Return the checkouts linked to this pickup
132
133 =cut
134
135 sub checkouts {
136     my ( $self ) = @_;
137
138     my @pi = Koha::CurbsidePickupIssues->search({ curbside_pickup_id => $self->id })->as_list;
139
140     my @checkouts = map { $_->checkout } @pi;
141     @checkouts = grep { defined $_ } @checkouts;
142
143     return @checkouts;
144 }
145
146 =head3 patron
147
148 Return the patron linked to this pickup
149
150 =cut
151
152 sub patron {
153     my ( $self ) = @_;
154     my $rs = $self->_result->borrowernumber;
155     return unless $rs;
156     return Koha::Patron->_new_from_dbic( $rs );
157 }
158
159 =head3 staged_by_staff
160
161 Return the staff member that staged this pickup
162
163 =cut
164
165 sub staged_by_staff {
166     my ( $self ) = @_;
167     my $rs = $self->_result->staged_by;
168     return unless $rs;
169     return Koha::Patron->_new_from_dbic( $rs );
170 }
171
172 =head3 library
173
174 Return the branch associated with this pickup
175
176 =cut
177
178 sub library {
179     my ( $self ) = @_;
180     my $rs = $self->_result->branchcode;
181     return unless $rs;
182     return Koha::Library->_new_from_dbic( $rs );
183 }
184
185 =head3 mark_as_staged
186
187 Mark the pickup as staged
188
189 =cut
190
191 sub mark_as_staged {
192     my ( $self ) = @_;
193     my $staged_by = C4::Context->userenv ? C4::Context->userenv->{number} : undef;
194     $self->set(
195         {
196             staged_datetime  => dt_from_string(),
197             staged_by        => $staged_by,
198             arrival_datetime => undef,
199         }
200     )->store;
201 }
202
203 =head3 mark_as_unstaged
204
205 Mark the pickup as unstaged
206
207 =cut
208
209 sub mark_as_unstaged {
210     my ( $self ) = @_;
211
212     $self->set(
213         {
214             staged_datetime  => undef,
215             staged_by        => undef,
216             arrival_datetime => undef,
217         }
218     )->store;
219 }
220
221 =head3 mark_patron_has_arrived
222
223 Set the arrival time of the patron
224
225 =cut
226
227 sub mark_patron_has_arrived {
228     my ( $self ) = @_;
229     $self->set(
230         {
231             arrival_datetime => dt_from_string(),
232         }
233     )->store;
234 }
235
236 =head3 mark_as_delivered
237
238 Mark the pickup as delivered. The waiting holds will be filled.
239
240 =cut
241
242 sub mark_as_delivered {
243     my ( $self ) = @_;
244     my $patron          = $self->patron;
245     my $holds           = $patron->holds;
246     my $branchcode = C4::Context->userenv ? C4::Context->userenv->{branch} : undef;
247     foreach my $hold ( $holds->as_list ) {
248         if ( $hold->found eq 'W' && $branchcode && $hold->branchcode eq $branchcode ) {
249             my ( $issuingimpossible, $needsconfirmation ) =
250               C4::Circulation::CanBookBeIssued( $patron, $hold->item->barcode );
251
252             unless ( keys %$issuingimpossible ) {
253                 my $issue =
254                   C4::Circulation::AddIssue( $patron->unblessed, $hold->item->barcode );
255                 if ($issue) {
256                     Koha::CurbsidePickupIssue->new(
257                         {
258                             curbside_pickup_id => $self->id,
259                             issue_id           => $issue->id,
260                             reserve_id         => $hold->id,
261                         }
262                     )->store();
263                 }
264                 else {
265                     Koha::Exceptions->throw(sprintf("Cannot checkout hold %s for patron %s: %s", $patron->id, $hold->id, join(", ", keys %$issuingimpossible)));
266                 }
267             }
268         }
269     }
270
271     my $delivered_by = C4::Context->userenv ? C4::Context->userenv->{number} : undef;
272     $self->arrival_datetime(dt_from_string) unless $self->arrival_datetime;
273     $self->set(
274         {
275             delivered_datetime => dt_from_string(),
276             delivered_by       => $delivered_by,
277         }
278     )->store;
279 }
280
281 =head3 status
282
283 Return the status of the pickup, can be 'to-be-staged', 'staged-and-ready', 'patron-is-outside' or 'delivered'.
284
285 =cut
286
287 sub status {
288     my ($self) = @_;
289     return
290         !defined $self->staged_datetime    ? 'to-be-staged'
291       : !defined $self->arrival_datetime   ? 'staged-and-ready'
292       : !defined $self->delivered_datetime ? 'patron-is-outside'
293       :                                      'delivered';
294 }
295
296 =head2 Internal methods
297
298 =head3 _type
299
300 =cut
301
302 sub _type {
303     return 'CurbsidePickup';
304 }
305
306 1;