3 # Copyright 2020 Aleisha Amohia <aleisha@catalyst.net.nz>
5 # This file is part of Koha.
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.
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.
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>.
23 use Koha::DateUtils qw( dt_from_string );
29 use base qw(Koha::Object);
33 Koha::Recall - Koha Recall Object class
43 my $biblio = $recall->biblio;
45 Returns the related Koha::Biblio object for this recall.
51 my $biblio_rs = $self->_result->biblio;
52 return unless $biblio_rs;
53 return Koha::Biblio->_new_from_dbic( $biblio_rs );
58 my $item = $recall->item;
60 Returns the related Koha::Item object for this recall.
66 my $item_rs = $self->_result->item;
67 return unless $item_rs;
68 return Koha::Item->_new_from_dbic( $item_rs );
73 my $patron = $recall->patron;
75 Returns the related Koha::Patron object for this recall.
81 my $patron_rs = $self->_result->patron;
82 return unless $patron_rs;
83 return Koha::Patron->_new_from_dbic( $patron_rs );
88 my $library = $recall->library;
90 Returns the related Koha::Library object for this recall.
96 my $library_rs = $self->_result->library;
97 return unless $library_rs;
98 return Koha::Library->_new_from_dbic( $library_rs );
103 my $checkout = $recall->checkout;
105 Returns the related Koha::Checkout object for this recall.
111 $self->{_checkout} ||= Koha::Checkouts->find({ itemnumber => $self->item_id });
113 unless ( $self->item_level ) {
114 # Only look at checkouts of items that are allowed to be recalled, and get the oldest one
115 my @items = Koha::Items->search({ biblionumber => $self->biblio_id })->as_list;
118 my $recalls_allowed = Koha::CirculationRules->get_effective_rule({
119 branchcode => C4::Context->userenv->{'branch'},
120 categorycode => $self->patron->categorycode,
121 itemtype => $_->effective_itemtype,
122 rule_name => 'recalls_allowed',
124 if ( defined $recalls_allowed and $recalls_allowed->rule_value > 0 ) {
125 push ( @itemnumbers, $_->itemnumber );
128 my $checkouts = Koha::Checkouts->search({ itemnumber => [ @itemnumbers ] }, { order_by => { -asc => 'date_due' } });
129 $self->{_checkout} = $checkouts->next;
132 return $self->{_checkout};
137 if ( $recall->requested )
139 [% IF recall.requested %]
141 Return true if recall status is requested.
147 return $self->status eq 'requested';
152 if ( $recall->waiting )
154 [% IF recall.waiting %]
156 Return true if recall is awaiting pickup.
162 return $self->status eq 'waiting';
167 if ( $recall->overdue )
169 [% IF recall.overdue %]
171 Return true if recall is overdue to be returned.
177 return $self->status eq 'overdue';
182 if ( $recall->in_transit )
184 [% IF recall.in_transit %]
186 Return true if recall is in transit.
192 return $self->status eq 'in_transit';
197 if ( $recall->expired )
199 [% IF recall.expired %]
201 Return true if recall has expired.
207 return $self->status eq 'expired';
212 if ( $recall->cancelled )
214 [% IF recall.cancelled %]
216 Return true if recall has been cancelled.
222 return $self->status eq 'cancelled';
227 if ( $recall->fulfilled )
229 [% IF recall.fulfilled %]
231 Return true if the recall has been fulfilled.
237 return $self->status eq 'fulfilled';
240 =head3 calc_expirationdate
242 my $expirationdate = $recall->calc_expirationdate;
243 $recall->update({ expirationdate => $expirationdate });
245 Calculate the expirationdate to set based on circulation rules and system preferences.
249 sub calc_expirationdate {
253 if ( $self->item_level ) {
255 } elsif ( $self->checkout ) {
256 $item = $self->checkout->item;
259 my $branchcode = $self->patron->branchcode;
261 $branchcode = C4::Circulation::_GetCircControlBranch( $item->unblessed, $self->patron->unblessed );
264 my $rule = Koha::CirculationRules->get_effective_rule({
265 categorycode => $self->patron->categorycode,
266 branchcode => $branchcode,
267 itemtype => $item ? $item->effective_itemtype : undef,
268 rule_name => 'recall_shelf_time'
271 my $shelf_time = defined $rule ? $rule->rule_value : C4::Context->preference('RecallsMaxPickUpDelay');
273 my $expirationdate = dt_from_string->add( days => $shelf_time );
274 return $expirationdate;
277 =head3 start_transfer
279 my ( $recall, $dotransfer, $messages ) = $recall->start_transfer({ item => $item_object });
281 Set the recall as in transit.
286 my ( $self, $params ) = @_;
288 if ( $self->item_level ) {
289 # already has an itemnumber
290 $self->update({ status => 'in_transit' });
292 my $itemnumber = $params->{item}->itemnumber;
293 $self->update({ status => 'in_transit', item_id => $itemnumber });
296 my ( $dotransfer, $messages ) = C4::Circulation::transferbook({ to_branch => $self->pickup_library_id, from_branch => $self->item->holdingbranch, barcode => $self->item->barcode, trigger => 'Recall' });
298 return ( $self, $dotransfer, $messages );
301 =head3 revert_transfer
303 $recall->revert_transfer;
305 If a transfer is cancelled, revert the recall to requested.
309 sub revert_transfer {
312 if ( $self->item_level ) {
313 $self->update({ status => 'requested' });
315 $self->update({ status => 'requested', item_id => undef });
323 $recall->set_waiting(
324 { expirationdate => $expirationdate,
329 Set the recall as waiting and update expiration date.
330 Notify the recall requester.
335 my ( $self, $params ) = @_;
338 if ( $self->item_level ) {
339 $itemnumber = $self->item_id;
340 $self->update({ status => 'waiting', waiting_date => dt_from_string, expiration_date => $params->{expirationdate} });
342 # biblio-level recall with no itemnumber. need to set itemnumber
343 $itemnumber = $params->{item}->itemnumber;
344 $self->update({ status => 'waiting', waiting_date => dt_from_string, expiration_date => $params->{expirationdate}, item_id => $itemnumber });
347 # send notice to recaller to pick up item
348 my $letter = C4::Letters::GetPreparedLetter(
349 module => 'circulation',
350 letter_code => 'PICKUP_RECALLED_ITEM',
351 branchcode => $self->pickup_library_id,
354 biblio => $self->biblio_id,
355 borrowers => $self->patron_id,
356 items => $itemnumber,
357 recalls => $self->recall_id,
361 C4::Message->enqueue($letter, $self->patron, 'email');
366 =head3 revert_waiting
368 $recall->revert_waiting;
370 Revert recall waiting status.
376 if ( $self->item_level ){
377 $self->update({ status => 'requested', waiting_date => undef });
379 $self->update({ status => 'requested', waiting_date => undef, item_id => undef });
384 =head3 should_be_overdue
386 if ( $recall->should_be_overdue ) {
387 $recall->set_overdue;
390 Return true if this recall should be marked overdue
394 sub should_be_overdue {
396 if ( $self->requested and $self->checkout and dt_from_string( $self->checkout->date_due ) <= dt_from_string ) {
404 $recall->set_overdue;
406 Set a recall as overdue when the recall has been requested and the borrower who has checked out the recalled item is late to return it. This can be done manually by the library or by cronjob. The interface is either 'INTRANET' or 'COMMANDLINE' for logging purposes.
411 my ( $self, $params ) = @_;
412 my $interface = $params->{interface} || 'COMMANDLINE';
413 $self->update({ status => 'overdue' });
414 C4::Log::logaction( 'RECALLS', 'OVERDUE', $self->id, "Recall status set to overdue", $interface ) if ( C4::Context->preference('RecallsLog') );
420 $recall->set_expired({ interface => 'INTRANET' });
422 Set a recall as expired. This may be done manually or by a cronjob, either when the borrower that placed the recall takes more than RecallsMaxPickUpDelay number of days to collect their item, or if the specified expirationdate passes. The interface is either 'INTRANET' or 'COMMANDLINE' for logging purposes.
427 my ( $self, $params ) = @_;
428 my $interface = $params->{interface} || 'COMMANDLINE';
429 $self->update({ status => 'expired', completed => 1, completed_date => dt_from_string });
430 C4::Log::logaction( 'RECALLS', 'EXPIRE', $self->id, "Recall expired", $interface ) if ( C4::Context->preference('RecallsLog') );
436 $recall->set_cancelled;
438 Set a recall as cancelled. This may be done manually, either by the borrower that placed the recall, or by the library.
444 $self->update({ status => 'cancelled', completed => 1, completed_date => dt_from_string });
445 C4::Log::logaction( 'RECALLS', 'CANCEL', $self->id, "Recall cancelled", 'INTRANET' ) if ( C4::Context->preference('RecallsLog') );
451 $recall->set_fulfilled;
453 Set a recall as finished. This should only be called when the item allocated to a recall is checked out to the borrower who requested the recall.
459 $self->update({ status => 'fulfilled', completed => 1, completed_date => dt_from_string });
460 C4::Log::logaction( 'RECALLS', 'FILL', $self->id, "Recall fulfilled", 'INTRANET' ) if ( C4::Context->preference('RecallsLog') );
464 =head2 Internal methods