|
Lines 64-74
sub age {
Link Here
|
| 64 |
my $age; |
64 |
my $age; |
| 65 |
|
65 |
|
| 66 |
if ( $use_calendar ) { |
66 |
if ( $use_calendar ) { |
| 67 |
my $calendar = Koha::Calendar->new( branchcode => $self->branchcode ); |
67 |
my $calendar = Koha::Calendar->new( branchcode => $self->pickup_library_id ); |
| 68 |
$age = $calendar->days_between( dt_from_string( $self->reservedate ), $today ); |
68 |
$age = $calendar->days_between( dt_from_string( $self->placing_date ), $today ); |
| 69 |
} |
69 |
} |
| 70 |
else { |
70 |
else { |
| 71 |
$age = $today->delta_days( dt_from_string( $self->reservedate ) ); |
71 |
$age = $today->delta_days( dt_from_string( $self->placing_date ) ); |
| 72 |
} |
72 |
} |
| 73 |
|
73 |
|
| 74 |
$age = $age->in_units( 'days' ); |
74 |
$age = $age->in_units( 'days' ); |
|
Lines 88-114
sub suspend_hold {
Link Here
|
| 88 |
my $date = $dt ? $dt->clone()->truncate( to => 'day' )->datetime : undef; |
88 |
my $date = $dt ? $dt->clone()->truncate( to => 'day' )->datetime : undef; |
| 89 |
|
89 |
|
| 90 |
if ( $self->is_found ) { # We can't suspend found holds |
90 |
if ( $self->is_found ) { # We can't suspend found holds |
| 91 |
if ( $self->is_waiting ) { |
91 |
if ( $self->is_waiting or |
| 92 |
Koha::Exceptions::Hold::CannotSuspendFound->throw( status => 'W' ); |
92 |
$self->is_in_transit ) { |
| 93 |
} |
93 |
Koha::Exceptions::Hold::CannotSuspendFound->throw( status => $self->status ); |
| 94 |
elsif ( $self->is_in_transit ) { |
|
|
| 95 |
Koha::Exceptions::Hold::CannotSuspendFound->throw( status => 'T' ); |
| 96 |
} |
94 |
} |
| 97 |
else { |
95 |
else { |
| 98 |
Koha::Exceptions::Hold::CannotSuspendFound->throw( |
96 |
Koha::Exceptions::Hold::CannotSuspendFound->throw( |
| 99 |
'Unhandled data exception on found hold (id=' |
97 |
'Unhandled data exception on found hold (id=' |
| 100 |
. $self->id |
98 |
. $self->id |
| 101 |
. ', found=' |
99 |
. ', status=' |
| 102 |
. $self->found |
100 |
. $self->status |
| 103 |
. ')' ); |
101 |
. ')' ); |
| 104 |
} |
102 |
} |
| 105 |
} |
103 |
} |
| 106 |
|
104 |
|
| 107 |
$self->suspend(1); |
105 |
$self->suspended(1); |
| 108 |
$self->suspend_until($date); |
106 |
$self->suspended_until_date($date); |
| 109 |
$self->store(); |
107 |
$self->store(); |
| 110 |
|
108 |
|
| 111 |
logaction( 'HOLDS', 'SUSPEND', $self->reserve_id, Dumper( $self->unblessed ) ) |
109 |
logaction( 'HOLDS', 'SUSPEND', $self->id, Dumper( $self->unblessed ) ) |
| 112 |
if C4::Context->preference('HoldsLog'); |
110 |
if C4::Context->preference('HoldsLog'); |
| 113 |
|
111 |
|
| 114 |
return $self; |
112 |
return $self; |
|
Lines 123-134
my $hold = $hold->resume();
Link Here
|
| 123 |
sub resume { |
121 |
sub resume { |
| 124 |
my ( $self ) = @_; |
122 |
my ( $self ) = @_; |
| 125 |
|
123 |
|
| 126 |
$self->suspend(0); |
124 |
$self->suspended(0); |
| 127 |
$self->suspend_until( undef ); |
125 |
$self->suspended_until_date( undef ); |
| 128 |
|
126 |
|
| 129 |
$self->store(); |
127 |
$self->store(); |
| 130 |
|
128 |
|
| 131 |
logaction( 'HOLDS', 'RESUME', $self->reserve_id, Dumper($self->unblessed) ) |
129 |
logaction( 'HOLDS', 'RESUME', $self->id, Dumper($self->unblessed) ) |
| 132 |
if C4::Context->preference('HoldsLog'); |
130 |
if C4::Context->preference('HoldsLog'); |
| 133 |
|
131 |
|
| 134 |
return $self; |
132 |
return $self; |
|
Lines 145-151
sub delete {
Link Here
|
| 145 |
|
143 |
|
| 146 |
my $deleted = $self->SUPER::delete($self); |
144 |
my $deleted = $self->SUPER::delete($self); |
| 147 |
|
145 |
|
| 148 |
logaction( 'HOLDS', 'DELETE', $self->reserve_id, Dumper($self->unblessed) ) |
146 |
logaction( 'HOLDS', 'DELETE', $self->id, Dumper($self->unblessed) ) |
| 149 |
if C4::Context->preference('HoldsLog'); |
147 |
if C4::Context->preference('HoldsLog'); |
| 150 |
|
148 |
|
| 151 |
return $deleted; |
149 |
return $deleted; |
|
Lines 161-184
sub set_waiting {
Link Here
|
| 161 |
$self->priority(0); |
159 |
$self->priority(0); |
| 162 |
|
160 |
|
| 163 |
if ($transferToDo) { |
161 |
if ($transferToDo) { |
| 164 |
$self->found('T')->store(); |
162 |
$self->status('in_transit')->store(); |
| 165 |
return $self; |
163 |
return $self; |
| 166 |
} |
164 |
} |
| 167 |
|
165 |
|
| 168 |
my $today = dt_from_string(); |
166 |
my $today = dt_from_string(); |
| 169 |
my $values = { |
167 |
my $values = { |
| 170 |
found => 'W', |
168 |
status => 'waiting', |
| 171 |
waitingdate => $today->ymd, |
169 |
waiting_date => $today->ymd, |
| 172 |
}; |
170 |
}; |
| 173 |
|
171 |
|
| 174 |
my $requested_expiration; |
172 |
my $requested_expiration; |
| 175 |
if ($self->expirationdate) { |
173 |
if ($self->expiration_date) { |
| 176 |
$requested_expiration = dt_from_string($self->expirationdate); |
174 |
$requested_expiration = dt_from_string($self->expiration_date); |
| 177 |
} |
175 |
} |
| 178 |
|
176 |
|
| 179 |
my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay"); |
177 |
my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay"); |
| 180 |
my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays'); |
178 |
my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays'); |
| 181 |
my $calendar = Koha::Calendar->new( branchcode => $self->branchcode ); |
179 |
my $calendar = Koha::Calendar->new( branchcode => $self->pickup_library_id ); |
| 182 |
|
180 |
|
| 183 |
my $expirationdate = $today->clone; |
181 |
my $expirationdate = $today->clone; |
| 184 |
$expirationdate->add(days => $max_pickup_delay); |
182 |
$expirationdate->add(days => $max_pickup_delay); |
|
Lines 190-196
sub set_waiting {
Link Here
|
| 190 |
# If patron's requested expiration date is prior to the |
188 |
# If patron's requested expiration date is prior to the |
| 191 |
# calculated one, we keep the patron's one. |
189 |
# calculated one, we keep the patron's one. |
| 192 |
my $cmp = $requested_expiration ? DateTime->compare($requested_expiration, $expirationdate) : 0; |
190 |
my $cmp = $requested_expiration ? DateTime->compare($requested_expiration, $expirationdate) : 0; |
| 193 |
$values->{expirationdate} = $cmp == -1 ? $requested_expiration->ymd : $expirationdate->ymd; |
191 |
$values->{expiration_date} = $cmp == -1 ? $requested_expiration->ymd : $expirationdate->ymd; |
| 194 |
|
192 |
|
| 195 |
$self->set($values)->store(); |
193 |
$self->set($values)->store(); |
| 196 |
|
194 |
|
|
Lines 206-214
Returns true if hold is a waiting or in transit
Link Here
|
| 206 |
sub is_found { |
204 |
sub is_found { |
| 207 |
my ($self) = @_; |
205 |
my ($self) = @_; |
| 208 |
|
206 |
|
| 209 |
return 0 unless $self->found(); |
207 |
if ( $self->status eq 'waiting' |
| 210 |
return 1 if $self->found() eq 'W'; |
208 |
or $self->status eq 'in_transit' ) |
| 211 |
return 1 if $self->found() eq 'T'; |
209 |
{ |
|
|
210 |
return 1; |
| 211 |
} |
| 212 |
|
| 213 |
return 0; |
| 212 |
} |
214 |
} |
| 213 |
|
215 |
|
| 214 |
=head3 is_waiting |
216 |
=head3 is_waiting |
|
Lines 220-227
Returns true if hold is a waiting hold
Link Here
|
| 220 |
sub is_waiting { |
222 |
sub is_waiting { |
| 221 |
my ($self) = @_; |
223 |
my ($self) = @_; |
| 222 |
|
224 |
|
| 223 |
my $found = $self->found; |
225 |
return $self->status eq 'waiting'; |
| 224 |
return $found && $found eq 'W'; |
|
|
| 225 |
} |
226 |
} |
| 226 |
|
227 |
|
| 227 |
=head3 is_in_transit |
228 |
=head3 is_in_transit |
|
Lines 233-240
Returns true if hold is a in_transit hold
Link Here
|
| 233 |
sub is_in_transit { |
234 |
sub is_in_transit { |
| 234 |
my ($self) = @_; |
235 |
my ($self) = @_; |
| 235 |
|
236 |
|
| 236 |
return 0 unless $self->found(); |
237 |
return $self->status eq 'in_transit'; |
| 237 |
return $self->found() eq 'T'; |
|
|
| 238 |
} |
238 |
} |
| 239 |
|
239 |
|
| 240 |
=head3 is_cancelable_from_opac |
240 |
=head3 is_cancelable_from_opac |
|
Lines 250-256
This is used from the OPAC.
Link Here
|
| 250 |
sub is_cancelable_from_opac { |
250 |
sub is_cancelable_from_opac { |
| 251 |
my ($self) = @_; |
251 |
my ($self) = @_; |
| 252 |
|
252 |
|
| 253 |
return 1 unless $self->is_found(); |
253 |
return 1 unless $self->is_found() or $self->completed; |
| 254 |
return 0; # if ->is_in_transit or if ->is_waiting |
254 |
return 0; # if ->is_in_transit or if ->is_waiting |
| 255 |
} |
255 |
} |
| 256 |
|
256 |
|
|
Lines 265-271
the hold item's holding branch
Link Here
|
| 265 |
sub is_at_destination { |
265 |
sub is_at_destination { |
| 266 |
my ($self) = @_; |
266 |
my ($self) = @_; |
| 267 |
|
267 |
|
| 268 |
return $self->is_waiting() && ( $self->branchcode() eq $self->item()->holdingbranch() ); |
268 |
return $self->is_waiting() && ( $self->pickup_library_id() eq $self->item()->holdingbranch() ); |
| 269 |
} |
269 |
} |
| 270 |
|
270 |
|
| 271 |
=head3 biblio |
271 |
=head3 biblio |
|
Lines 277-283
Returns the related Koha::Biblio object for this hold
Link Here
|
| 277 |
sub biblio { |
277 |
sub biblio { |
| 278 |
my ($self) = @_; |
278 |
my ($self) = @_; |
| 279 |
|
279 |
|
| 280 |
$self->{_biblio} ||= Koha::Biblios->find( $self->biblionumber() ); |
280 |
$self->{_biblio} ||= Koha::Biblios->find( $self->biblio_id ); |
| 281 |
|
281 |
|
| 282 |
return $self->{_biblio}; |
282 |
return $self->{_biblio}; |
| 283 |
} |
283 |
} |
|
Lines 291-297
Returns the related Koha::Item object for this Hold
Link Here
|
| 291 |
sub item { |
291 |
sub item { |
| 292 |
my ($self) = @_; |
292 |
my ($self) = @_; |
| 293 |
|
293 |
|
| 294 |
$self->{_item} ||= Koha::Items->find( $self->itemnumber() ); |
294 |
$self->{_item} ||= Koha::Items->find( $self->item_id ); |
| 295 |
|
295 |
|
| 296 |
return $self->{_item}; |
296 |
return $self->{_item}; |
| 297 |
} |
297 |
} |
|
Lines 305-311
Returns the related Koha::Library object for this Hold
Link Here
|
| 305 |
sub branch { |
305 |
sub branch { |
| 306 |
my ($self) = @_; |
306 |
my ($self) = @_; |
| 307 |
|
307 |
|
| 308 |
$self->{_branch} ||= Koha::Libraries->find( $self->branchcode() ); |
308 |
$self->{_branch} ||= Koha::Libraries->find( $self->pickup_library_id ); |
| 309 |
|
309 |
|
| 310 |
return $self->{_branch}; |
310 |
return $self->{_branch}; |
| 311 |
} |
311 |
} |
|
Lines 320-326
Returns the related Koha::Patron object for this Hold
Link Here
|
| 320 |
sub borrower { |
320 |
sub borrower { |
| 321 |
my ($self) = @_; |
321 |
my ($self) = @_; |
| 322 |
|
322 |
|
| 323 |
$self->{_borrower} ||= Koha::Patrons->find( $self->borrowernumber() ); |
323 |
$self->{_borrower} ||= Koha::Patrons->find( $self->patron_id ); |
| 324 |
|
324 |
|
| 325 |
return $self->{_borrower}; |
325 |
return $self->{_borrower}; |
| 326 |
} |
326 |
} |
|
Lines 334-340
my $bool = $hold->is_suspended();
Link Here
|
| 334 |
sub is_suspended { |
334 |
sub is_suspended { |
| 335 |
my ( $self ) = @_; |
335 |
my ( $self ) = @_; |
| 336 |
|
336 |
|
| 337 |
return $self->suspend(); |
337 |
return $self->suspended(); |
| 338 |
} |
338 |
} |
| 339 |
|
339 |
|
| 340 |
|
340 |
|
|
Lines 354-372
sub cancel {
Link Here
|
| 354 |
my ( $self, $params ) = @_; |
354 |
my ( $self, $params ) = @_; |
| 355 |
$self->_result->result_source->schema->txn_do( |
355 |
$self->_result->result_source->schema->txn_do( |
| 356 |
sub { |
356 |
sub { |
| 357 |
$self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) ); |
357 |
$self->set( |
| 358 |
$self->priority(0); |
358 |
{ |
| 359 |
$self->_move_to_old; |
359 |
completion_date => dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ), |
| 360 |
$self->SUPER::delete(); # Do not add a DELETE log |
360 |
priority => 0, |
|
|
361 |
completed => 1, |
| 362 |
status => 'cancelled' |
| 363 |
} |
| 364 |
)->store; |
| 361 |
|
365 |
|
| 362 |
# now fix the priority on the others.... |
366 |
# now fix the priority on the others.... |
| 363 |
C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber }); |
367 |
C4::Reserves::_FixPriority({ biblionumber => $self->biblio_id }); |
| 364 |
|
368 |
|
| 365 |
# and, if desired, charge a cancel fee |
369 |
# and, if desired, charge a cancel fee |
| 366 |
my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge"); |
370 |
my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge"); |
| 367 |
if ( $charge && $params->{'charge_cancel_fee'} ) { |
371 |
if ( $charge && $params->{'charge_cancel_fee'} ) { |
| 368 |
my $account = |
372 |
my $account = |
| 369 |
Koha::Account->new( { patron_id => $self->borrowernumber } ); |
373 |
Koha::Account->new( { patron_id => $self->patron_id } ); |
| 370 |
$account->add_debit( |
374 |
$account->add_debit( |
| 371 |
{ |
375 |
{ |
| 372 |
amount => $charge, |
376 |
amount => $charge, |
|
Lines 374-405
sub cancel {
Link Here
|
| 374 |
interface => C4::Context->interface, |
378 |
interface => C4::Context->interface, |
| 375 |
library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, |
379 |
library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, |
| 376 |
type => 'RESERVE_EXPIRED', |
380 |
type => 'RESERVE_EXPIRED', |
| 377 |
item_id => $self->itemnumber |
381 |
item_id => $self->item_id |
| 378 |
} |
382 |
} |
| 379 |
); |
383 |
); |
| 380 |
} |
384 |
} |
| 381 |
|
385 |
|
| 382 |
C4::Log::logaction( 'HOLDS', 'CANCEL', $self->reserve_id, Dumper($self->unblessed) ) |
386 |
C4::Log::logaction( 'HOLDS', 'CANCEL', $self->id, Dumper($self->unblessed) ) |
| 383 |
if C4::Context->preference('HoldsLog'); |
387 |
if C4::Context->preference('HoldsLog'); |
| 384 |
} |
388 |
} |
| 385 |
); |
389 |
); |
| 386 |
return $self; |
390 |
return $self; |
| 387 |
} |
391 |
} |
| 388 |
|
392 |
|
| 389 |
=head3 _move_to_old |
|
|
| 390 |
|
| 391 |
my $is_moved = $hold->_move_to_old; |
| 392 |
|
| 393 |
Move a hold to the old_reserve table following the same pattern as Koha::Patron->move_to_deleted |
| 394 |
|
| 395 |
=cut |
| 396 |
|
| 397 |
sub _move_to_old { |
| 398 |
my ($self) = @_; |
| 399 |
my $hold_infos = $self->unblessed; |
| 400 |
return Koha::Old::Hold->new( $hold_infos )->store; |
| 401 |
} |
| 402 |
|
| 403 |
=head3 to_api_mapping |
393 |
=head3 to_api_mapping |
| 404 |
|
394 |
|
| 405 |
This method returns the mapping for representing a Koha::Hold object |
395 |
This method returns the mapping for representing a Koha::Hold object |
|
Lines 409-432
on the API.
Link Here
|
| 409 |
|
399 |
|
| 410 |
sub to_api_mapping { |
400 |
sub to_api_mapping { |
| 411 |
return { |
401 |
return { |
| 412 |
reserve_id => 'hold_id', |
402 |
id => 'hold_id', |
| 413 |
borrowernumber => 'patron_id', |
|
|
| 414 |
reservedate => 'hold_date', |
| 415 |
biblionumber => 'biblio_id', |
| 416 |
branchcode => 'pickup_library_id', |
| 417 |
notificationdate => undef, |
| 418 |
reminderdate => undef, |
| 419 |
cancellationdate => 'cancelation_date', |
| 420 |
reservenotes => 'notes', |
| 421 |
found => 'status', |
| 422 |
itemnumber => 'item_id', |
| 423 |
waitingdate => 'waiting_date', |
| 424 |
expirationdate => 'expiration_date', |
| 425 |
lowestPriority => 'lowest_priority', |
| 426 |
suspend => 'suspended', |
| 427 |
suspend_until => 'suspended_until', |
| 428 |
itemtype => 'item_type', |
| 429 |
item_level_hold => 'item_level', |
| 430 |
}; |
403 |
}; |
| 431 |
} |
404 |
} |
| 432 |
|
405 |
|
|
Lines 437-443
sub to_api_mapping {
Link Here
|
| 437 |
=cut |
410 |
=cut |
| 438 |
|
411 |
|
| 439 |
sub _type { |
412 |
sub _type { |
| 440 |
return 'Reserve'; |
413 |
return 'Hold'; |
| 441 |
} |
414 |
} |
| 442 |
|
415 |
|
| 443 |
=head1 AUTHORS |
416 |
=head1 AUTHORS |