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

(-)a/C4/Letters.pm (+1 lines)
Lines 757-762 sub _parseletter_sth { Link Here
757
    ($table eq 'subscription') ? "SELECT * FROM $table WHERE subscriptionid = ?" :
757
    ($table eq 'subscription') ? "SELECT * FROM $table WHERE subscriptionid = ?" :
758
    ($table eq 'serial') ? "SELECT * FROM $table WHERE serialid = ?" :
758
    ($table eq 'serial') ? "SELECT * FROM $table WHERE serialid = ?" :
759
    ($table eq 'problem_reports') ? "SELECT * FROM $table WHERE reportid = ?" :
759
    ($table eq 'problem_reports') ? "SELECT * FROM $table WHERE reportid = ?" :
760
    ($table eq 'recalls') ? "SELECT * FROM $table WHERE recall_id = ?" :
760
    undef ;
761
    undef ;
761
    unless ($query) {
762
    unless ($query) {
762
        warn "ERROR: No _parseletter_sth query for table '$table'";
763
        warn "ERROR: No _parseletter_sth query for table '$table'";
(-)a/C4/Stats.pm (-1 / +1 lines)
Lines 83-89 sub UpdateStats { Link Here
83
    return () if ! defined $params;
83
    return () if ! defined $params;
84
# change these arrays if new types of transaction or new parameters are allowed
84
# change these arrays if new types of transaction or new parameters are allowed
85
    my @allowed_keys = qw (type branch amount other itemnumber itemtype borrowernumber ccode location);
85
    my @allowed_keys = qw (type branch amount other itemnumber itemtype borrowernumber ccode location);
86
    my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout);
86
    my @allowed_circulation_types = qw (renew issue localuse return onsite_checkout recall);
87
    my @allowed_accounts_types = qw (writeoff payment);
87
    my @allowed_accounts_types = qw (writeoff payment);
88
    my @circulation_mandatory_keys = qw (type branch borrowernumber itemnumber ccode itemtype);
88
    my @circulation_mandatory_keys = qw (type branch borrowernumber itemnumber ccode itemtype);
89
    my @accounts_mandatory_keys = qw (type branch borrowernumber amount);
89
    my @accounts_mandatory_keys = qw (type branch borrowernumber amount);
(-)a/Koha/Recall.pm (+424 lines)
Line 0 Link Here
1
package Koha::Recall;
2
3
# Copyright 2020 Aleisha Amohia <aleisha@catalyst.net.nz>
4
#
5
# This file is part of Koha.
6
#
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.
11
#
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.
16
#
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>.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::DateUtils;
24
25
use base qw(Koha::Object);
26
27
=head1 NAME
28
29
Koha::Recall - Koha Recall Object class
30
31
=head1 API
32
33
=head2 Internal methods
34
35
=cut
36
37
=head3 biblio
38
39
    my $biblio = $recall->biblio;
40
41
Returns the related Koha::Biblio object for this recall.
42
43
=cut
44
45
sub biblio {
46
    my ( $self ) = @_;
47
    my $biblio_rs = $self->_result->biblio;
48
    return Koha::Biblio->_new_from_dbic( $biblio_rs );
49
}
50
51
=head3 item
52
53
    my $item = $recall->item;
54
55
Returns the related Koha::Item object for this recall.
56
57
=cut
58
59
sub item {
60
    my ( $self ) = @_;
61
    my $item_rs = $self->_result->item;
62
    if ( $item_rs ){
63
        return Koha::Item->_new_from_dbic( $item_rs );
64
    }
65
    return;
66
}
67
68
=head3 patron
69
70
    my $patron = $recall->patron;
71
72
Returns the related Koha::Patron object for this recall.
73
74
=cut
75
76
sub patron {
77
    my ( $self ) = @_;
78
    my $patron_rs = $self->_result->borrower;
79
    return Koha::Patron->_new_from_dbic( $patron_rs );
80
}
81
82
=head3 library
83
84
    my $library = $recall->library;
85
86
Returns the related Koha::Library object for this recall.
87
88
=cut
89
90
sub library {
91
    my ( $self ) = @_;
92
    my $library_rs = $self->_result->branch;
93
    return Koha::Library->_new_from_dbic( $library_rs );
94
}
95
96
=head3 checkout
97
98
    my $checkout = $recall->checkout;
99
100
Returns the related Koha::Checkout object for this recall.
101
102
=cut
103
104
sub checkout {
105
    my ( $self ) = @_;
106
    $self->{_checkout} ||= Koha::Checkouts->find({ itemnumber => $self->itemnumber });
107
108
    unless ( $self->item_level_recall ) {
109
        # Only look at checkouts of items that are allowed to be recalled, and get the oldest one
110
        my @items = Koha::Items->search({ biblionumber => $self->biblionumber });
111
        my @itemnumbers;
112
        foreach (@items) {
113
            my $recalls_allowed = Koha::CirculationRules->get_effective_rule({
114
                branchcode => C4::Context->userenv->{'branch'},
115
                categorycode => $self->patron->categorycode,
116
                itemtype => $_->effective_itemtype,
117
                rule_name => 'recalls_allowed',
118
            });
119
            if ( defined $recalls_allowed and $recalls_allowed->rule_value > 0 ) {
120
                push ( @itemnumbers, $_->itemnumber );
121
            }
122
        }
123
        my $checkouts = Koha::Checkouts->search({ itemnumber => [ @itemnumbers ] }, { order_by => { -asc => 'date_due' } });
124
        $self->{_checkout} = $checkouts->next;
125
    }
126
127
    return $self->{_checkout};
128
}
129
130
=head3 requested
131
132
    if ( $recall->requested )
133
134
    [% IF recall.requested %]
135
136
Return true if recall status is requested.
137
138
=cut
139
140
sub requested {
141
    my ( $self ) = @_;
142
    my $status = $self->status;
143
    return $status && $status eq 'R';
144
}
145
146
=head3 waiting
147
148
    if ( $recall->waiting )
149
150
    [% IF recall.waiting %]
151
152
Return true if recall is awaiting pickup.
153
154
=cut
155
156
sub waiting {
157
    my ( $self ) = @_;
158
    my $status = $self->status;
159
    return $status && $status eq 'W';
160
}
161
162
=head3 overdue
163
164
    if ( $recall->overdue )
165
166
    [% IF recall.overdue %]
167
168
Return true if recall is overdue to be returned.
169
170
=cut
171
172
sub overdue {
173
    my ( $self ) = @_;
174
    my $status = $self->status;
175
    return $status && $status eq 'O';
176
}
177
178
=head3 in_transit
179
180
    if ( $recall->in_transit )
181
182
    [% IF recall.in_transit %]
183
184
Return true if recall is in transit.
185
186
=cut
187
188
sub in_transit {
189
    my ( $self ) = @_;
190
    my $status = $self->status;
191
    return $status && $status eq 'T';
192
}
193
194
=head3 expired
195
196
    if ( $recall->expired )
197
198
    [% IF recall.expired %]
199
200
Return true if recall has expired.
201
202
=cut
203
204
sub expired {
205
    my ( $self ) = @_;
206
    my $status = $self->status;
207
    return $status && $status eq 'E';
208
}
209
210
=head3 cancelled
211
212
    if ( $recall->cancelled )
213
214
    [% IF recall.cancelled %]
215
216
Return true if recall has been cancelled.
217
218
=cut
219
220
sub cancelled {
221
    my ( $self ) = @_;
222
    my $status = $self->status;
223
    return $status && $status eq 'C';
224
}
225
226
=head3 finished
227
228
    if ( $recall->finished )
229
230
    [% IF recall.finished %]
231
232
Return true if recall is finished and has been fulfilled.
233
234
=cut
235
236
sub finished {
237
    my ( $self ) = @_;
238
    my $status = $self->status;
239
    return $status && $status eq 'F';
240
}
241
242
=head3 calc_expirationdate
243
244
    my $expirationdate = $recall->calc_expirationdate;
245
    $recall->update({ expirationdate => $expirationdate });
246
247
Calculate the expirationdate to set based on circulation rules and system preferences.
248
249
=cut
250
251
sub calc_expirationdate {
252
    my ( $self ) = @_;
253
254
    my $branchcode = C4::Circulation::_GetCircControlBranch( $self->item->unblessed, $self->patron->unblessed );
255
    my $rule = Koha::CirculationRules->get_effective_rule({
256
        categorycode => $self->patron->categorycode,
257
        branchcode => $branchcode,
258
        itemtype => $self->item->effective_itemtype,
259
        rule_name => 'recall_shelf_time'
260
    });
261
262
    my $shelf_time = defined $rule ? $rule->rule_value : C4::Context->preference('RecallsMaxPickUpDelay');
263
264
    my $expirationdate = dt_from_string->add( days => $shelf_time );
265
    return $expirationdate;
266
}
267
268
=head3 start_transfer
269
270
    $recall->start_transfer({ item => $item_object });
271
272
Set the recall as in transit.
273
274
=cut
275
276
sub start_transfer {
277
    my ( $self, $params ) = @_;
278
279
    if ( $self->item_level_recall ) {
280
        # already has an itemnumber
281
        $self->update({ status => 'T' });
282
    } else {
283
        my $itemnumber = $params->{item}->itemnumber;
284
        $self->update({ status => 'T', itemnumber => $itemnumber });
285
    }
286
287
    my $ignore_reserves = 1;
288
    my ( $dotransfer, $messages ) = C4::Circulation::transferbook( $self->branchcode, $self->item->barcode, $ignore_reserves, 'Recalled' );
289
290
    return $self;
291
}
292
293
=head3 set_waiting
294
295
    $recall->set_waiting({
296
        expirationdate => $expirationdate,
297
        item => $item_object
298
    });
299
300
Set the recall as waiting and update expiration date.
301
Notify the recall requester.
302
303
=cut
304
305
sub set_waiting {
306
    my ( $self, $params ) = @_;
307
308
    my $itemnumber;
309
    if ( $self->item_level_recall ) {
310
        $itemnumber = $self->itemnumber;
311
        $self->update({ status => 'W', waitingdate => dt_from_string, expirationdate => $params->{expirationdate} });
312
    } else {
313
        # biblio-level recall with no itemnumber. need to set itemnumber
314
        $itemnumber = $params->{item}->itemnumber;
315
        $self->update({ status => 'W', waitingdate => dt_from_string, expirationdate => $params->{expirationdate}, itemnumber => $itemnumber });
316
    }
317
318
    # send notice to recaller to pick up item
319
    my $letter = C4::Letters::GetPreparedLetter(
320
        module => 'circulation',
321
        letter_code => 'PICKUP_RECALLED_ITEM',
322
        branchcode => $self->branchcode,
323
        want_librarian => 0,
324
        tables => {
325
            biblio => $self->biblionumber,
326
            borrowers => $self->borrowernumber,
327
            items => $itemnumber,
328
            recalls => $self->recall_id,
329
        },
330
    );
331
332
    C4::Message->enqueue($letter, $self->patron->unblessed, 'email');
333
334
    return $self;
335
}
336
337
=head3 revert_waiting
338
339
    $recall->revert_waiting;
340
341
Revert recall waiting status.
342
343
=cut
344
345
sub revert_waiting {
346
    my ( $self ) = @_;
347
    if ( $self->item_level_recall ){
348
        $self->update({ status => 'R', waitingdate => undef });
349
    } else {
350
        $self->update({ status => 'R', waitingdate => undef, itemnumber => undef });
351
    }
352
    return $self;
353
}
354
355
=head3 set_overdue
356
357
    $recall->set_overdue;
358
359
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 will only be used by a cron - a recall cannot be set as overdue manually.
360
361
=cut
362
363
sub set_overdue {
364
    my ( $self ) = @_;
365
    $self->update({ status => 'O' });
366
    C4::Log::logaction( 'RECALLS', 'OVERDUE', $self->recall_id, "Recall status set to overdue", 'COMMANDLINE') if ( C4::Context->preference('RecallsLog') );
367
    return $self;
368
}
369
370
=head3 set_expired
371
372
    $recall->set_expired({ location => 'INTRANET' });
373
374
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 location is either 'INTRANET' or 'COMMANDLINE' for logging purposes.
375
376
=cut
377
378
sub set_expired {
379
    my ( $self, $params ) = @_;
380
    my $location = $params->{location} || 'COMMANDLINE';
381
    $self->update({ status => 'E', old => 1, expirationdate => dt_from_string });
382
    C4::Log::logaction( 'RECALLS', 'EXPIRE', $self->recall_id, "Recall expired", $location) if ( C4::Context->preference('RecallsLog') );
383
    return $self;
384
}
385
386
=head3 set_cancelled
387
388
    $recall->set_cancelled;
389
390
Set a recall as cancelled. This may be done manually, either by the borrower that placed the recall, or by the library.
391
392
=cut
393
394
sub set_cancelled {
395
    my ( $self ) = @_;
396
    $self->update({ status => 'C', old => 1, cancellationdate => dt_from_string });
397
    C4::Log::logaction( 'RECALLS', 'CANCEL', $self->recall_id, "Recall cancelled", 'INTRANET') if ( C4::Context->preference('RecallsLog') );
398
    return $self;
399
}
400
401
=head3 set_finished
402
403
    $recall->set_finished;
404
405
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.
406
407
=cut
408
409
sub set_finished {
410
    my ( $self ) = @_;
411
    $self->update({ status => 'F', old => 1 });
412
    C4::Log::logaction( 'RECALLS', 'FULFILL', $self->recall_id, "Recall fulfilled", 'INTRANET') if ( C4::Context->preference('RecallsLog') );
413
    return $self;
414
}
415
416
=head3 _type
417
418
=cut
419
420
sub _type {
421
    return 'Recall';
422
}
423
424
1;
(-)a/Koha/Recalls.pm (+159 lines)
Line 0 Link Here
1
package Koha::Recalls;
2
3
# Copyright 2020 Aleisha Amohia <aleisha@catalyst.net.nz>
4
#
5
# This file is part of Koha.
6
#
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.
11
#
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.
16
#
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>.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Recall;
24
use Koha::DateUtils;
25
26
use C4::Stats;
27
28
use base qw(Koha::Objects);
29
30
=head1 NAME
31
32
Koha::Recalls - Koha Recalls Object set class
33
34
=head1 API
35
36
=head2 Internal methods
37
38
=cut
39
40
=head3 add_recall
41
42
    my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
43
        patron => $patron_object,
44
        biblio => $biblio_object,
45
        branchcode => $branchcode,
46
        item => $item_object,
47
        expirationdate => $expirationdate,
48
        interface => 'OPAC',
49
    });
50
51
Add a new requested recall. We assume at this point that a recall is allowed to be placed on this item or biblio. We are past the checks and are now doing the recall.
52
Interface param is either OPAC or INTRANET
53
Send a RETURN_RECALLED_ITEM notice.
54
Add statistics and logs.
55
#FIXME: Add recallnotes and priority when staff-side recalls is added
56
57
=cut
58
59
sub add_recall {
60
    my ( $self, $params ) = @_;
61
62
    my $patron = $params->{patron};
63
    my $biblio = $params->{biblio};
64
    return if ( !defined($patron) or !defined($biblio) );
65
    my $branchcode = $params->{branchcode};
66
    $branchcode ||= $patron->branchcode;
67
    my $item = $params->{item};
68
    my $itemnumber = $item ? $item->itemnumber : undef;
69
    my $expirationdate = $params->{expirationdate};
70
    my $interface = $params->{interface};
71
72
    if ( $expirationdate ){
73
        my $now = dt_from_string;
74
        $expirationdate = dt_from_string($expirationdate)->set({ hour => $now->hour, minute => $now->minute, second => $now->second });
75
    }
76
77
    my $recall_request = Koha::Recall->new({
78
        borrowernumber => $patron->borrowernumber,
79
        recalldate => dt_from_string(),
80
        biblionumber => $biblio->biblionumber,
81
        branchcode => $branchcode,
82
        status => 'R',
83
        itemnumber => defined $itemnumber ? $itemnumber : undef,
84
        expirationdate => $expirationdate,
85
        item_level_recall => defined $itemnumber ? 1 : 0,
86
    })->store;
87
88
    if (defined $recall_request->recall_id){ # successful recall
89
        my $recall = Koha::Recalls->find( $recall_request->recall_id );
90
91
        # get checkout and adjust due date based on circulation rules
92
        my $checkout = $recall->checkout;
93
        my $recall_due_date_interval = Koha::CirculationRules->get_effective_rule({
94
            categorycode => $checkout->patron->categorycode,
95
            itemtype => $checkout->item->effective_itemtype,
96
            branchcode => $branchcode,
97
            rule_name => 'recall_due_date_interval',
98
        });
99
        my $due_interval = defined $recall_due_date_interval ? $recall_due_date_interval->rule_value : 5;
100
        my $timestamp = dt_from_string( $recall->timestamp );
101
        my $due_date = $timestamp->add( days => $due_interval );
102
        $checkout->update({ date_due => $due_date });
103
104
        # get itemnumber of most relevant checkout if a biblio-level recall
105
        unless ( $recall->item_level_recall ) { $itemnumber = $checkout->itemnumber; }
106
107
        # send notice to user with recalled item checked out
108
        my $letter = C4::Letters::GetPreparedLetter (
109
            module => 'circulation',
110
            letter_code => 'RETURN_RECALLED_ITEM',
111
            branchcode => $recall->branchcode,
112
            tables => {
113
                biblio => $biblio->biblionumber,
114
                borrowers => $checkout->borrowernumber,
115
                items => $itemnumber,
116
                issues => $itemnumber,
117
            },
118
        );
119
120
        C4::Message->enqueue( $letter, $checkout->patron->unblessed, 'email' );
121
122
        $item = Koha::Items->find( $itemnumber );
123
        # add to statistics table
124
        UpdateStats({
125
            branch => C4::Context->userenv->{'branch'},
126
            type => 'recall',
127
            itemnumber => $itemnumber,
128
            borrowernumber => $recall->borrowernumber,
129
            itemtype => $item->effective_itemtype,
130
            ccode => $item->ccode,
131
        });
132
133
        # add action log
134
        C4::Log::logaction( 'RECALLS', 'CREATE', $recall->recall_id, "Recall requested by borrower #" . $recall->borrowernumber, $interface ) if ( C4::Context->preference('RecallsLog') );
135
136
        return ( $recall, $due_interval, $due_date );
137
    }
138
139
    # unable to add recall
140
    return;
141
}
142
143
=head3 _type
144
145
=cut
146
147
sub _type {
148
    return 'Recall';
149
}
150
151
=head3 object_class
152
153
=cut
154
155
sub object_class {
156
    return 'Koha::Recall';
157
}
158
159
1;
(-)a/Koha/Schema/Result/Recall.pm (+38 lines)
Lines 273-276 __PACKAGE__->add_columns( Link Here
273
    '+item_level_recall' => { is_boolean => 1 },
273
    '+item_level_recall' => { is_boolean => 1 },
274
);
274
);
275
275
276
__PACKAGE__->belongs_to(
277
  "biblio",
278
  "Koha::Schema::Result::Biblio",
279
  { biblionumber => "biblionumber" },
280
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
281
);
282
283
__PACKAGE__->belongs_to(
284
  "item",
285
  "Koha::Schema::Result::Item",
286
  { itemnumber => "itemnumber" },
287
  {
288
    is_deferrable => 1,
289
    join_type     => "LEFT",
290
    on_delete     => "CASCADE",
291
    on_update     => "CASCADE",
292
  },
293
);
294
295
__PACKAGE__->belongs_to(
296
  "borrower",
297
  "Koha::Schema::Result::Borrower",
298
  { borrowernumber => "borrowernumber" },
299
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
300
);
301
302
__PACKAGE__->belongs_to(
303
  "branch",
304
  "Koha::Schema::Result::Branch",
305
  { branchcode => "branchcode" },
306
  {
307
    is_deferrable => 1,
308
    join_type     => "LEFT",
309
    on_delete     => "CASCADE",
310
    on_update     => "CASCADE",
311
  },
312
);
313
276
1;
314
1;
(-)a/t/db_dependent/Koha/Recall.t (+179 lines)
Line 0 Link Here
1
#!/usr/bin/perl
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 Test::More tests => 22;
21
use t::lib::TestBuilder;
22
use t::lib::Mocks;
23
24
use Koha::DateUtils;
25
26
BEGIN {
27
    require_ok('Koha::Recall');
28
    require_ok('Koha::Recalls');
29
}
30
31
# Start transaction
32
33
my $database = Koha::Database->new();
34
my $schema = $database->schema();
35
$schema->storage->txn_begin();
36
my $dbh = C4::Context->dbh;
37
38
my $builder = t::lib::TestBuilder->new;
39
40
# Setup test variables
41
42
my $item1 = $builder->build_sample_item();
43
my $biblio1 = $item1->biblio;
44
my $branch1 = $item1->holdingbranch;
45
my $itemtype1 = $item1->effective_itemtype;
46
47
my $item2 = $builder->build_sample_item();
48
my $biblio2 = $item2->biblio;
49
my $branch2 = $item2->holdingbranch;
50
my $itemtype2 = $item2->effective_itemtype;
51
52
my $category1 = $builder->build({ source => 'Category' })->{ categorycode };
53
my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category1, branchcode => $branch1 } });
54
my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category1, branchcode => $branch1 } });
55
t::lib::Mocks::mock_userenv({ patron => $patron1 });
56
my $old_recalls_count = Koha::Recalls->search({ old => 1 })->count;
57
58
Koha::CirculationRules->set_rule({
59
    branchcode => undef,
60
    categorycode => undef,
61
    itemtype => undef,
62
    rule_name => 'recalls_allowed',
63
    rule_value => '10',
64
});
65
66
C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
67
68
my $recall1 = Koha::Recall->new({
69
    borrowernumber => $patron1->borrowernumber,
70
    recalldate => dt_from_string,
71
    biblionumber => $biblio1->biblionumber,
72
    branchcode => $branch1,
73
    status => 'R',
74
    itemnumber => $item1->itemnumber,
75
    expirationdate => undef,
76
    item_level_recall => 1
77
})->store;
78
79
is( $recall1->biblio->title, $biblio1->title, "Recall biblio relationship correctly linked" );
80
is( $recall1->item->homebranch, $item1->homebranch, "Recall item relationship correctly linked" );
81
is( $recall1->patron->categorycode, $category1, "Recall patron relationship correctly linked" );
82
is( $recall1->library->branchname, Koha::Libraries->find( $branch1 )->branchname, "Recall library relationship correctly linked" );
83
is( $recall1->checkout->itemnumber, $item1->itemnumber, "Recall checkout relationship correctly linked" );
84
is( $recall1->requested, 1, "Recall has been requested" );
85
86
$recall1->set_overdue;
87
is( $recall1->overdue, 1, "Recall is overdue" );
88
89
$recall1->set_cancelled;
90
is( $recall1->cancelled, 1, "Recall is cancelled" );
91
92
my $recall2 = Koha::Recall->new({
93
    borrowernumber => $patron1->borrowernumber,
94
    recalldate => dt_from_string,
95
    biblionumber => $biblio1->biblionumber,
96
    branchcode => $branch1,
97
    status => 'R',
98
    itemnumber => $item1->itemnumber,
99
    expirationdate => undef,
100
    item_level_recall => 1
101
})->store;
102
103
Koha::CirculationRules->set_rule({
104
    branchcode => undef,
105
    categorycode => undef,
106
    itemtype => undef,
107
    rule_name => 'recall_shelf_time',
108
    rule_value => undef,
109
});
110
111
t::lib::Mocks->mock_preference( 'RecallsMaxPickUpDelay', 7 );
112
my $expected_expirationdate = dt_from_string->add({ days => 7 });
113
my $expirationdate = $recall2->calc_expirationdate;
114
is( $expirationdate, $expected_expirationdate, "Expiration date calculated based on system preference as no circulation rules are set" );
115
116
Koha::CirculationRules->set_rule({
117
    branchcode => undef,
118
    categorycode => undef,
119
    itemtype => undef,
120
    rule_name => 'recall_shelf_time',
121
    rule_value => '3',
122
});
123
$expected_expirationdate = dt_from_string->add({ days => 3 });
124
$expirationdate = $recall2->calc_expirationdate;
125
is( $expirationdate, $expected_expirationdate, "Expiration date calculated based on circulation rules" );
126
127
$recall2->set_waiting({ expirationdate => $expirationdate });
128
is( $recall2->waiting, 1, "Recall is waiting" );
129
130
my $notice = C4::Message->find_last_message( $patron1->unblessed, 'PICKUP_RECALLED_ITEM', 'email' );
131
ok( defined $notice, "Patron was notified to pick up waiting recall" );
132
133
$recall2->set_expired({ location => 'COMMANDLINE' });
134
is( $recall2->expired, 1, "Recall has expired" );
135
136
my $old_recalls_count_now = Koha::Recalls->search({ old => 1 })->count;
137
is( $old_recalls_count_now, $old_recalls_count + 2, "Recalls have been flagged as old when cancelled or expired" );
138
139
my $recall3 = Koha::Recall->new({
140
    borrowernumber => $patron1->borrowernumber,
141
    recalldate => dt_from_string,
142
    biblionumber => $biblio1->biblionumber,
143
    branchcode => $branch1,
144
    status => 'R',
145
    itemnumber => $item1->itemnumber,
146
    expirationdate => undef,
147
    item_level_recall => 1
148
})->store;
149
150
# test that recall gets T status
151
$recall3->start_transfer;
152
is( $recall3->in_transit, 1, "Recall is in transit" );
153
154
# for testing purposes, pretend the item gets checked out
155
$recall3->set_finished;
156
is( $recall3->finished, 1, "Recall has been fulfilled" );
157
158
C4::Circulation::AddIssue( $patron2->unblessed, $item1->barcode );
159
my $recall4 = Koha::Recall->new({
160
    borrowernumber => $patron1->borrowernumber,
161
    recalldate => dt_from_string,
162
    biblionumber => $biblio1->biblionumber,
163
    branchcode => $branch1,
164
    status => 'R',
165
    itemnumber => undef,
166
    expirationdate => undef,
167
    item_level_recall => 0,
168
})->store;
169
170
ok( !defined $recall4->item, "No relevant item returned for a biblio-level recall" );
171
is( $recall4->checkout->itemnumber, $item1->itemnumber, "Return most relevant checkout for a biblio-level recall");
172
173
$recall4->set_waiting({ item => $item1, expirationdate => $expirationdate });
174
is( $recall4->itemnumber, $item1->itemnumber, "Item has been allocated to biblio-level recall" );
175
176
$recall4->revert_waiting;
177
ok( !defined $recall4->itemnumber, "Itemnumber has been removed from biblio-level recall when reverting waiting status" );
178
179
$schema->storage->txn_rollback();
(-)a/t/db_dependent/Koha/Recalls.t (-1 / +145 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
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 Test::More tests => 12;
21
use t::lib::TestBuilder;
22
use t::lib::Mocks;
23
24
use Koha::DateUtils;
25
26
BEGIN {
27
    require_ok('Koha::Recall');
28
    require_ok('Koha::Recalls');
29
}
30
31
# Start transaction
32
33
my $database = Koha::Database->new();
34
my $schema = $database->schema();
35
$schema->storage->txn_begin();
36
my $dbh = C4::Context->dbh;
37
38
my $builder = t::lib::TestBuilder->new;
39
40
# Setup test variables
41
42
my $item1 = $builder->build_sample_item();
43
my $biblio1 = $item1->biblio;
44
my $branch1 = $item1->holdingbranch;
45
my $itemtype1 = $item1->effective_itemtype;
46
my $item2 = $builder->build_sample_item();
47
my $biblio2 = $item1->biblio;
48
my $branch2 = $item1->holdingbranch;
49
my $itemtype2 = $item1->effective_itemtype;
50
51
my $category1 = $builder->build({ source => 'Category' })->{ categorycode };
52
my $patron1 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category1, branchcode => $branch1 } });
53
my $patron2 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category1, branchcode => $branch2 } });
54
my $patron3 = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category1, branchcode => $branch1 } });
55
t::lib::Mocks::mock_userenv({ patron => $patron1 });
56
57
Koha::CirculationRules->set_rules({
58
    branchcode => undef,
59
    categorycode => undef,
60
    itemtype => undef,
61
    rules => {
62
        'recall_due_date_interval' => undef,
63
        'recalls_allowed' => 10,
64
    }
65
});
66
67
C4::Circulation::AddIssue( $patron3->unblessed, $item1->barcode );
68
C4::Circulation::AddIssue( $patron3->unblessed, $item2->barcode );
69
70
my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
71
    patron => undef,
72
    biblio => $biblio1,
73
    branchcode => $branch1,
74
    item => $item1,
75
    expirationdate => undef,
76
    interface => 'COMMANDLINE',
77
});
78
ok( !defined $recall, "Can't add a recall without specifying a patron" );
79
80
( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
81
    patron => $patron1,
82
    biblio => undef,
83
    branchcode => $branch1,
84
    item => $item1,
85
    expirationdate => undef,
86
    interface => 'COMMANDLINE',
87
});
88
ok( !defined $recall, "Can't add a recall without specifying a biblio" );
89
90
( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
91
    patron => $patron1,
92
    biblio => undef,
93
    branchcode => $branch1,
94
    item => $item1,
95
    expirationdate => undef,
96
    interface => 'COMMANDLINE',
97
});
98
ok( !defined $recall, "Can't add a recall without specifying a biblio" );
99
100
( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
101
    patron => $patron2,
102
    biblio => $biblio1,
103
    branchcode => undef,
104
    item => $item2,
105
    expirationdate => undef,
106
    interface => 'COMMANDLINE',
107
});
108
is( $recall->branchcode, $branch2, "No pickup branch specified so patron branch used" );
109
is( $due_interval, 5, "Recall due date interval defaults to 5 if not specified" );
110
111
Koha::CirculationRules->set_rule({
112
    branchcode => undef,
113
    categorycode => undef,
114
    itemtype => undef,
115
    rule_name => 'recall_due_date_interval',
116
    rule_value => 3,
117
});
118
( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
119
    patron => $patron1,
120
    biblio => $biblio1,
121
    branchcode => undef,
122
    item => $item1,
123
    expirationdate => undef,
124
    interface => 'COMMANDLINE',
125
});
126
is( $due_interval, 3, "Recall due date interval is based on circulation rules" );
127
128
( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({
129
    patron => $patron1,
130
    biblio => $biblio1,
131
    branchcode => $branch1,
132
    item => undef,
133
    expirationdate => undef,
134
    interface => 'COMMANDLINE',
135
});
136
is( $recall->item_level_recall, 0, "No item provided so recall not flagged as item-level" );
137
138
my $expected_due_date = dt_from_string->add( days => 3 );
139
is( dt_from_string( $recall->checkout->date_due ), $expected_due_date, "Checkout due date has correctly been extended by recall_due_date_interval days" );
140
is( $due_date, $expected_due_date, "Due date correctly returned" );
141
142
my $messages_count = Koha::Notice::Messages->search({ borrowernumber => $patron3->borrowernumber, letter_code => 'RETURN_RECALLED_ITEM' })->count;
143
is( $messages_count, 3, "RETURN_RECALLED_ITEM notice successfully sent to checkout borrower" );
144
145
$schema->storage->txn_rollback();

Return to bug 19532