View | Details | Raw Unified | Return to bug 25260
Collapse All | Expand All

(-)a/Koha/Hold.pm (-80 / +52 lines)
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->created_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->created_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-343 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
341
=head3 cancel
340
=head3 cancel
342
341
343
my $cancel_hold = $hold->cancel();
342
my $cancel_hold = $hold->cancel();
Lines 354-372 sub cancel { Link Here
354
    my ( $self, $params ) = @_;
353
    my ( $self, $params ) = @_;
355
    $self->_result->result_source->schema->txn_do(
354
    $self->_result->result_source->schema->txn_do(
356
        sub {
355
        sub {
357
            $self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) );
356
            $self->set(
358
            $self->priority(0);
357
                {
359
            $self->_move_to_old;
358
                    completed_date => dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ),
360
            $self->SUPER::delete(); # Do not add a DELETE log
359
                    priority        => 0,
360
                    completed       => 1,
361
                    status          => 'cancelled'
362
                }
363
            )->store;
361
364
362
            # now fix the priority on the others....
365
            # now fix the priority on the others....
363
            C4::Reserves::_FixPriority({ biblionumber => $self->biblionumber });
366
            C4::Reserves::_FixPriority({ biblionumber => $self->biblio_id });
364
367
365
            # and, if desired, charge a cancel fee
368
            # and, if desired, charge a cancel fee
366
            my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
369
            my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
367
            if ( $charge && $params->{'charge_cancel_fee'} ) {
370
            if ( $charge && $params->{'charge_cancel_fee'} ) {
368
                my $account =
371
                my $account =
369
                  Koha::Account->new( { patron_id => $self->borrowernumber } );
372
                  Koha::Account->new( { patron_id => $self->patron_id } );
370
                $account->add_debit(
373
                $account->add_debit(
371
                    {
374
                    {
372
                        amount     => $charge,
375
                        amount     => $charge,
Lines 374-405 sub cancel { Link Here
374
                        interface  => C4::Context->interface,
377
                        interface  => C4::Context->interface,
375
                        library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef,
378
                        library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef,
376
                        type       => 'RESERVE_EXPIRED',
379
                        type       => 'RESERVE_EXPIRED',
377
                        item_id    => $self->itemnumber
380
                        item_id    => $self->item_id
378
                    }
381
                    }
379
                );
382
                );
380
            }
383
            }
381
384
382
            C4::Log::logaction( 'HOLDS', 'CANCEL', $self->reserve_id, Dumper($self->unblessed) )
385
            C4::Log::logaction( 'HOLDS', 'CANCEL', $self->id, Dumper($self->unblessed) )
383
                if C4::Context->preference('HoldsLog');
386
                if C4::Context->preference('HoldsLog');
384
        }
387
        }
385
    );
388
    );
386
    return $self;
389
    return $self;
387
}
390
}
388
391
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
392
=head3 to_api_mapping
404
393
405
This method returns the mapping for representing a Koha::Hold object
394
This method returns the mapping for representing a Koha::Hold object
Lines 409-432 on the API. Link Here
409
398
410
sub to_api_mapping {
399
sub to_api_mapping {
411
    return {
400
    return {
412
        reserve_id       => 'hold_id',
401
        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
    };
402
    };
431
}
403
}
432
404
Lines 437-443 sub to_api_mapping { Link Here
437
=cut
409
=cut
438
410
439
sub _type {
411
sub _type {
440
    return 'Reserve';
412
    return 'Hold';
441
}
413
}
442
414
443
=head1 AUTHORS
415
=head1 AUTHORS
(-)a/Koha/Holds.pm (-7 / +16 lines)
Lines 33-39 Koha::Holds - Koha Hold object set class Link Here
33
33
34
=head1 API
34
=head1 API
35
35
36
=head2 Class Methods
36
=head2 Class methods
37
37
38
=cut
38
=cut
39
39
Lines 46-52 returns a set of holds that are waiting from an existing set Link Here
46
sub waiting {
46
sub waiting {
47
    my ( $self ) = @_;
47
    my ( $self ) = @_;
48
48
49
    return $self->search( { found => 'W' } );
49
    return $self->search(
50
        {
51
            status    => 'waiting',
52
            completed => 0
53
        }
54
    );
50
}
55
}
51
56
52
=head3 unfilled
57
=head3 unfilled
Lines 58-64 returns a set of holds that are unfilled from an existing set Link Here
58
sub unfilled {
63
sub unfilled {
59
    my ( $self ) = @_;
64
    my ( $self ) = @_;
60
65
61
    return $self->search( { found => undef } );
66
    return $self->search(
67
        {
68
            status    => 'placed',
69
            completed => 0
70
        }
71
    );
62
}
72
}
63
73
64
=head3 forced_hold_level
74
=head3 forced_hold_level
Lines 83-92 that would prevent one or the other. Link Here
83
sub forced_hold_level {
93
sub forced_hold_level {
84
    my ($self) = @_;
94
    my ($self) = @_;
85
95
86
    my $item_level_count = $self->search( { itemnumber => { '!=' => undef } } )->count();
96
    my $item_level_count = $self->search( { item_id => { '!=' => undef } } )->count();
87
    return 'item' if $item_level_count > 0;
97
    return 'item' if $item_level_count > 0;
88
98
89
    my $record_level_count = $self->search( { itemnumber => undef } )->count();
99
    my $record_level_count = $self->search( { item_id => undef } )->count();
90
    return 'record' if $record_level_count > 0;
100
    return 'record' if $record_level_count > 0;
91
101
92
    return;
102
    return;
Lines 97-103 sub forced_hold_level { Link Here
97
=cut
107
=cut
98
108
99
sub _type {
109
sub _type {
100
    return 'Reserve';
110
    return 'Hold';
101
}
111
}
102
112
103
=head3 object_class
113
=head3 object_class
104
- 

Return to bug 25260