Bug 30650: Notify the patron when a new curbside pickup is created
[koha-ffzg.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::DateUtils qw( dt_from_string );
30 use Koha::Patron;
31 use Koha::Library;
32 use Koha::CurbsidePickupIssues;
33 use Koha::Exceptions::CurbsidePickup;
34
35 =head1 NAME
36
37 Koha::CurbsidePickup - Koha Curbside Pickup Object class
38
39 =head1 API
40
41 =head2 Class methods
42
43 =cut
44
45 =head3 new
46
47 =cut
48
49 sub new {
50     my ( $self, $params ) = @_;
51
52     my $existing_curbside_pickups = Koha::CurbsidePickups->search(
53         {
54             branchcode                => $params->{branchcode},
55             borrowernumber            => $params->{borrowernumber},
56             delivered_datetime        => undef,
57             scheduled_pickup_datetime => { '>' => \'DATE(NOW())' },
58         }
59     );
60     Koha::Exceptions::CurbsidePickup::TooManyPickups->throw(
61         branchcode     => $params->{branchcode},
62         borrowernumber => $params->{borrowernumber}
63     ) if $existing_curbside_pickups->count;
64
65     my $policy =
66       Koha::CurbsidePickupPolicies->find( { branchcode => $params->{branchcode} } );
67     my $is_valid =
68       $policy->is_valid_pickup_datetime( $params->{scheduled_pickup_datetime} );
69     unless ($is_valid) {
70         my $error = @{ $is_valid->messages }[0]->message;
71         Koha::Exceptions::CurbsidePickup::NoMatchingSlots->throw
72           if $error eq 'no_matching_slots';
73         Koha::Exceptions::CurbsidePickup::NoMorePickupsAvailable->throw
74           if $error eq 'no_more_available';
75         Koha::Exceptions->throw(
76             "Error message must raise the appropriate exception");
77     }
78
79     return $self->SUPER::new($params);
80 }
81
82 =head3 notify_new_pickup
83
84 $pickup->notify_new_pickup
85
86 Will notify the patron that the pickup has been created.
87 Letter 'NEW_CURBSIDE_PICKUP will be used', and depending on 'Hold_Filled' configuration.
88
89 =cut
90
91 sub notify_new_pickup {
92     my ( $self ) = @_;
93
94     my $patron = $self->patron;
95
96     my $library = $self->library;
97
98     $patron->queue_notice({ letter_params => {
99         module     => 'reserves',
100         letter_code => 'NEW_CURBSIDE_PICKUP',
101         borrowernumber => $patron->borrowernumber,
102         branchcode => $self->branchcode,
103         tables     => {
104             'branches'  => $library->unblessed,
105             'borrowers' => $patron->unblessed,
106         },
107         substitute => {
108             curbside_pickup => $self,
109         }
110     }, message_name => 'Hold_Filled' });
111 }
112
113 =head3 checkouts
114
115 Return the checkouts linked to this pickup
116
117 =cut
118
119 sub checkouts {
120     my ( $self ) = @_;
121
122     my @pi = Koha::CurbsidePickupIssues->search({ curbside_pickup_id => $self->id })->as_list;
123
124     my @checkouts = map { $_->checkout } @pi;
125     @checkouts = grep { defined $_ } @checkouts;
126
127     return @checkouts;
128 }
129
130 =head3 patron
131
132 Return the patron linked to this pickup
133
134 =cut
135
136 sub patron {
137     my ( $self ) = @_;
138     my $rs = $self->_result->borrowernumber;
139     return unless $rs;
140     return Koha::Patron->_new_from_dbic( $rs );
141 }
142
143 =head3 staged_by_staff
144
145 Return the staff member that staged this pickup
146
147 =cut
148
149 sub staged_by_staff {
150     my ( $self ) = @_;
151     my $rs = $self->_result->staged_by;
152     return unless $rs;
153     return Koha::Patron->_new_from_dbic( $rs );
154 }
155
156 =head3 library
157
158 Return the branch associated with this pickup
159
160 =cut
161
162 sub library {
163     my ( $self ) = @_;
164     my $rs = $self->_result->branchcode;
165     return unless $rs;
166     return Koha::Library->_new_from_dbic( $rs );
167 }
168
169 =head3 mark_as_staged
170
171 Mark the pickup as staged
172
173 =cut
174
175 sub mark_as_staged {
176     my ( $self ) = @_;
177     my $staged_by = C4::Context->userenv ? C4::Context->userenv->{number} : undef;
178     $self->set(
179         {
180             staged_datetime  => dt_from_string(),
181             staged_by        => $staged_by,
182             arrival_datetime => undef,
183         }
184     )->store;
185 }
186
187 =head3 mark_as_unstaged
188
189 Mark the pickup as unstaged
190
191 =cut
192
193 sub mark_as_unstaged {
194     my ( $self ) = @_;
195
196     $self->set(
197         {
198             staged_datetime  => undef,
199             staged_by        => undef,
200             arrival_datetime => undef,
201         }
202     )->store;
203 }
204
205 =head3 mark_patron_has_arrived
206
207 Set the arrival time of the patron
208
209 =cut
210
211 sub mark_patron_has_arrived {
212     my ( $self ) = @_;
213     $self->set(
214         {
215             arrival_datetime => dt_from_string(),
216         }
217     )->store;
218 }
219
220 =head3 mark_as_delivered
221
222 Mark the pickup as delivered. The waiting holds will be filled.
223
224 =cut
225
226 sub mark_as_delivered {
227     my ( $self ) = @_;
228     my $patron          = $self->patron;
229     my $holds           = $patron->holds;
230     my $branchcode = C4::Context->userenv ? C4::Context->userenv->{branch} : undef;
231     foreach my $hold ( $holds->as_list ) {
232         if ( $hold->found eq 'W' && $branchcode && $hold->branchcode eq $branchcode ) {
233             my ( $issuingimpossible, $needsconfirmation ) =
234               C4::Circulation::CanBookBeIssued( $patron, $hold->item->barcode );
235
236             unless ( keys %$issuingimpossible ) {
237                 my $issue =
238                   C4::Circulation::AddIssue( $patron->unblessed, $hold->item->barcode );
239                 if ($issue) {
240                     Koha::CurbsidePickupIssue->new(
241                         {
242                             curbside_pickup_id => $self->id,
243                             issue_id           => $issue->id,
244                             reserve_id         => $hold->id,
245                         }
246                     )->store();
247                 }
248                 else {
249                     Koha::Exceptions->throw(sprintf("Cannot checkout hold %s for patron %s: %s", $patron->id, $hold->id, join(", ", keys %$issuingimpossible)));
250                 }
251             }
252         }
253     }
254
255     my $delivered_by = C4::Context->userenv ? C4::Context->userenv->{number} : undef;
256     $self->arrival_datetime(dt_from_string) unless $self->arrival_datetime;
257     $self->set(
258         {
259             delivered_datetime => dt_from_string(),
260             delivered_by       => $delivered_by,
261         }
262     )->store;
263 }
264
265 =head3 status
266
267 Return the status of the pickup, can be 'to-be-staged', 'staged-and-ready', 'patron-is-outside' or 'delivered'.
268
269 =cut
270
271 sub status {
272     my ($self) = @_;
273     return
274         !defined $self->staged_datetime    ? 'to-be-staged'
275       : !defined $self->arrival_datetime   ? 'staged-and-ready'
276       : !defined $self->delivered_datetime ? 'patron-is-outside'
277       :                                      'delivered';
278 }
279
280 =head2 Internal methods
281
282 =head3 _type
283
284 =cut
285
286 sub _type {
287     return 'CurbsidePickup';
288 }
289
290 1;