Bug 24860: (QA follow-up) Fix number of tests in t/db_dependent/Koha/Holds.t
[koha.git] / t / db_dependent / Koha / Holds.t
1 #!/usr/bin/perl
2
3 # Copyright 2020 Koha Development team
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 Test::More tests => 9;
23 use Test::Warn;
24
25 use C4::Circulation qw( AddIssue );
26 use C4::Reserves qw( AddReserve ModReserve ModReserveCancelAll );
27 use Koha::AuthorisedValueCategory;
28 use Koha::Biblio::ItemGroups;
29 use Koha::Database;
30 use Koha::DateUtils qw( dt_from_string );
31 use Koha::Holds;
32
33 use t::lib::Mocks;
34 use t::lib::TestBuilder;
35
36 my $schema = Koha::Database->new->schema;
37 $schema->storage->txn_begin;
38
39 my $builder = t::lib::TestBuilder->new;
40
41 subtest 'DB constraints' => sub {
42     plan tests => 1;
43
44     my $patron = $builder->build_object({ class => 'Koha::Patrons' });
45     my $item = $builder->build_sample_item;
46     my $hold_info = {
47         branchcode     => $patron->branchcode,
48         borrowernumber => $patron->borrowernumber,
49         biblionumber   => $item->biblionumber,
50         priority       => 1,
51         title          => "title for fee",
52         itemnumber     => $item->itemnumber,
53     };
54
55     my $reserve_id = C4::Reserves::AddReserve($hold_info);
56     my $hold = Koha::Holds->find( $reserve_id );
57
58     warning_like {
59         eval { $hold->priority(undef)->store }
60     }
61     qr{.*DBD::mysql::st execute failed: Column 'priority' cannot be null.*},
62       'DBD should have raised an error about priority that cannot be null';
63 };
64
65 subtest 'cancel' => sub {
66     plan tests => 12;
67     my $biblioitem = $builder->build_object( { class => 'Koha::Biblioitems' } );
68     my $library    = $builder->build_object( { class => 'Koha::Libraries' } );
69     my $itemtype   = $builder->build_object( { class => 'Koha::ItemTypes', value => { rentalcharge => 0 } } );
70     my $item_info  = {
71         biblionumber     => $biblioitem->biblionumber,
72         biblioitemnumber => $biblioitem->biblioitemnumber,
73         homebranch       => $library->branchcode,
74         holdingbranch    => $library->branchcode,
75         itype            => $itemtype->itemtype,
76     };
77     my $item = $builder->build_object( { class => 'Koha::Items', value => $item_info } );
78     my $manager = $builder->build_object({ class => "Koha::Patrons" });
79     t::lib::Mocks::mock_userenv({ patron => $manager,branchcode => $manager->branchcode });
80
81     my ( @patrons, @holds );
82     for my $i ( 0 .. 2 ) {
83         my $priority = $i + 1;
84         my $patron   = $builder->build_object(
85             {
86                 class => 'Koha::Patrons',
87                 value => { branchcode => $library->branchcode, }
88             }
89         );
90         my $reserve_id = C4::Reserves::AddReserve(
91             {
92                 branchcode     => $library->branchcode,
93                 borrowernumber => $patron->borrowernumber,
94                 biblionumber   => $item->biblionumber,
95                 priority       => $priority,
96                 title          => "title for fee",
97                 itemnumber     => $item->itemnumber,
98             }
99         );
100         my $hold = Koha::Holds->find($reserve_id);
101         push @patrons, $patron;
102         push @holds,   $hold;
103     }
104
105     # There are 3 holds on this records
106     my $nb_of_holds =
107       Koha::Holds->search( { biblionumber => $item->biblionumber } )->count;
108     is( $nb_of_holds, 3,
109         'There should have 3 holds placed on this biblio record' );
110     my $first_hold  = $holds[0];
111     my $second_hold = $holds[1];
112     my $third_hold  = $holds[2];
113     is( ref($second_hold), 'Koha::Hold',
114         'We should play with Koha::Hold objects' );
115     is( $second_hold->priority, 2,
116         'Second hold should have a priority set to 3' );
117
118     # Remove the second hold, only 2 should still exist in DB and priorities must have been updated
119     my $is_cancelled = $second_hold->cancel;
120     is( ref($is_cancelled), 'Koha::Hold',
121         'Koha::Hold->cancel should return the Koha::Hold (?)' )
122       ;    # This is can reconsidered
123     is( $second_hold->in_storage, 0,
124         'The hold has been cancelled and does not longer exist in DB' );
125     $nb_of_holds =
126       Koha::Holds->search( { biblionumber => $item->biblionumber } )->count;
127     is( $nb_of_holds, 2,
128         'a hold has been cancelled, there should have only 2 holds placed on this biblio record'
129     );
130
131     # discard_changes to refetch
132     is( $first_hold->discard_changes->priority, 1, 'First hold should still be first' );
133     is( $third_hold->discard_changes->priority, 2, 'Third hold should now be second' );
134
135     subtest 'charge_cancel_fee parameter' => sub {
136         plan tests => 4;
137         my $patron_category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { reservefee => 0 } } );
138         my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $patron_category->categorycode } });
139         is( $patron->account->balance, 0, 'A new patron does not have any charges' );
140
141         my $hold_info = {
142             branchcode     => $library->branchcode,
143             borrowernumber => $patron->borrowernumber,
144             biblionumber   => $item->biblionumber,
145             priority       => 1,
146             title          => "title for fee",
147             itemnumber     => $item->itemnumber,
148         };
149
150         # First, test cancelling a reserve when there's no charge configured.
151         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 0);
152         my $reserve_id = C4::Reserves::AddReserve( $hold_info );
153         Koha::Holds->find( $reserve_id )->cancel( { charge_cancel_fee => 1 } );
154         is( $patron->account->balance, 0, 'ExpireReservesMaxPickUpDelayCharge=0 - The patron should not have been charged' );
155
156         # Then, test cancelling a reserve when there's no charge desired.
157         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 42);
158         $reserve_id = C4::Reserves::AddReserve( $hold_info );
159         Koha::Holds->find( $reserve_id )->cancel(); # charge_cancel_fee => 0
160         is( $patron->account->balance, 0, 'ExpireReservesMaxPickUpDelayCharge=42, but charge_cancel_fee => 0, The patron should not have been charged' );
161
162
163         # Finally, test cancelling a reserve when there's a charge desired and configured.
164         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 42);
165         $reserve_id = C4::Reserves::AddReserve( $hold_info );
166         Koha::Holds->find( $reserve_id )->cancel( { charge_cancel_fee => 1 } );
167         is( int($patron->account->balance), 42, 'ExpireReservesMaxPickUpDelayCharge=42 and charge_cancel_fee => 1, The patron should have been charged!' );
168     };
169
170     subtest 'waiting hold' => sub {
171         plan tests => 1;
172         my $patron = $builder->build_object({ class => 'Koha::Patrons' });
173         my $reserve_id = C4::Reserves::AddReserve(
174             {
175                 branchcode     => $library->branchcode,
176                 borrowernumber => $patron->borrowernumber,
177                 biblionumber   => $item->biblionumber,
178                 priority       => 1,
179                 title          => "title for fee",
180                 itemnumber     => $item->itemnumber,
181                 found          => 'W',
182             }
183         );
184         Koha::Holds->find( $reserve_id )->cancel;
185         my $hold_old = Koha::Old::Holds->find( $reserve_id );
186         is( $hold_old->found, 'W', 'The found column should have been kept and a hold is cancelled' );
187     };
188
189     subtest 'HoldsLog' => sub {
190         plan tests => 2;
191         my $patron = $builder->build_object({ class => 'Koha::Patrons' });
192         my $hold_info = {
193             branchcode     => $library->branchcode,
194             borrowernumber => $patron->borrowernumber,
195             biblionumber   => $item->biblionumber,
196             priority       => 1,
197             title          => "title for fee",
198             itemnumber     => $item->itemnumber,
199         };
200
201         t::lib::Mocks::mock_preference('HoldsLog', 0);
202         my $reserve_id = C4::Reserves::AddReserve($hold_info);
203         Koha::Holds->find( $reserve_id )->cancel;
204         my $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'HOLDS', action => 'CANCEL', object => $reserve_id } )->count;
205         is( $number_of_logs, 0, 'Without HoldsLog, Koha::Hold->cancel should not have logged' );
206
207         t::lib::Mocks::mock_preference('HoldsLog', 1);
208         $reserve_id = C4::Reserves::AddReserve($hold_info);
209         Koha::Holds->find( $reserve_id )->cancel;
210         $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'HOLDS', action => 'CANCEL', object => $reserve_id } )->count;
211         is( $number_of_logs, 1, 'With HoldsLog, Koha::Hold->cancel should have logged' );
212     };
213
214     subtest 'rollback' => sub {
215         plan tests => 3;
216         my $patron_category = $builder->build_object(
217             {
218                 class => 'Koha::Patron::Categories',
219                 value => { reservefee => 0 }
220             }
221         );
222         my $patron = $builder->build_object(
223             {
224                 class => 'Koha::Patrons',
225                 value => { categorycode => $patron_category->categorycode }
226             }
227         );
228         my $hold_info = {
229             branchcode     => $library->branchcode,
230             borrowernumber => $patron->borrowernumber,
231             biblionumber   => $item->biblionumber,
232             priority       => 1,
233             title          => "title for fee",
234             itemnumber     => $item->itemnumber,
235         };
236
237         t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelayCharge',42 );
238         my $reserve_id = C4::Reserves::AddReserve($hold_info);
239         my $hold       = Koha::Holds->find($reserve_id);
240
241         # Add a row with the same id to make the cancel fails
242         Koha::Old::Hold->new( $hold->unblessed )->store;
243
244         warning_like {
245             eval { $hold->cancel( { charge_cancel_fee => 1 } ) };
246         }
247         qr{.*DBD::mysql::st execute failed: Duplicate entry.*},
248           'DBD should have raised an error about dup primary key';
249
250         $hold = Koha::Holds->find($reserve_id);
251         is( ref($hold), 'Koha::Hold', 'The hold should not have been deleted' );
252         is( $patron->account->balance, 0,
253 'If the hold has not been cancelled, the patron should not have been charged'
254         );
255     };
256
257 };
258
259 subtest 'cancel with reason' => sub {
260     plan tests => 7;
261     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
262     my $item = $builder->build_sample_item({ library => $library->branchcode });
263     my $manager = $builder->build_object( { class => "Koha::Patrons" } );
264     t::lib::Mocks::mock_userenv( { patron => $manager, branchcode => $manager->branchcode } );
265
266     my $patron = $builder->build_object(
267         {
268             class => 'Koha::Patrons',
269             value => { branchcode => $library->branchcode, }
270         }
271     );
272
273     my $reserve_id = C4::Reserves::AddReserve(
274         {
275             branchcode     => $library->branchcode,
276             borrowernumber => $patron->borrowernumber,
277             biblionumber   => $item->biblionumber,
278             priority       => 1,
279             itemnumber     => $item->itemnumber,
280         }
281     );
282
283     my $hold = Koha::Holds->find($reserve_id);
284
285     ok($reserve_id, "Hold created");
286     ok($hold, "Hold found");
287
288     my $av = Koha::AuthorisedValue->new( { category => 'HOLD_CANCELLATION', authorised_value => 'TEST_REASON' } )->store;
289     Koha::Notice::Templates->search({ code => 'HOLD_CANCELLATION'})->delete();
290     my $notice = Koha::Notice::Template->new({
291         name                   => 'Hold cancellation',
292         module                 => 'reserves',
293         code                   => 'HOLD_CANCELLATION',
294         title                  => 'Hold cancelled',
295         content                => 'Your hold was cancelled.',
296         message_transport_type => 'email',
297         branchcode             => q{},
298     })->store();
299
300     $hold->cancel({cancellation_reason => 'TEST_REASON'});
301
302     $hold = Koha::Holds->find($reserve_id);
303     is( $hold, undef, 'Hold is not in the reserves table');
304     $hold = Koha::Old::Holds->find($reserve_id);
305     ok( $hold, 'Hold was found in the old reserves table');
306
307     my $message = Koha::Notice::Messages->find({ borrowernumber => $patron->id, letter_code => 'HOLD_CANCELLATION'});
308     ok( $message, 'Found hold cancellation message');
309     is( $message->subject, 'Hold cancelled', 'Message has correct title' );
310     is( $message->content, 'Your hold was cancelled.', 'Message has correct content');
311
312     $notice->delete;
313     $av->delete;
314     $message->delete;
315 };
316
317 subtest 'cancel all with reason' => sub {
318     plan tests => 7;
319     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
320     my $item = $builder->build_sample_item({ library => $library->branchcode });
321     my $manager = $builder->build_object( { class => "Koha::Patrons" } );
322     t::lib::Mocks::mock_userenv( { patron => $manager, branchcode => $manager->branchcode } );
323
324     my $patron = $builder->build_object(
325         {
326             class => 'Koha::Patrons',
327             value => { branchcode => $library->branchcode, }
328         }
329     );
330
331     my $reserve_id = C4::Reserves::AddReserve(
332         {
333             branchcode     => $library->branchcode,
334             borrowernumber => $patron->borrowernumber,
335             biblionumber   => $item->biblionumber,
336             priority       => 1,
337             itemnumber     => $item->itemnumber,
338         }
339     );
340
341     my $hold = Koha::Holds->find($reserve_id);
342
343     ok($reserve_id, "Hold created");
344     ok($hold, "Hold found");
345
346     my $av = Koha::AuthorisedValue->new( { category => 'HOLD_CANCELLATION', authorised_value => 'TEST_REASON' } )->store;
347     Koha::Notice::Templates->search({ code => 'HOLD_CANCELLATION'})->delete();
348     my $notice = Koha::Notice::Template->new({
349         name                   => 'Hold cancellation',
350         module                 => 'reserves',
351         code                   => 'HOLD_CANCELLATION',
352         title                  => 'Hold cancelled',
353         content                => 'Your hold was cancelled.',
354         message_transport_type => 'email',
355         branchcode             => q{},
356     })->store();
357
358     ModReserveCancelAll($item->id, $patron->id, 'TEST_REASON');
359
360     $hold = Koha::Holds->find($reserve_id);
361     is( $hold, undef, 'Hold is not in the reserves table');
362     $hold = Koha::Old::Holds->find($reserve_id);
363     ok( $hold, 'Hold was found in the old reserves table');
364
365     my $message = Koha::Notice::Messages->find({ borrowernumber => $patron->id, letter_code => 'HOLD_CANCELLATION'});
366     ok( $message, 'Found hold cancellation message');
367     is( $message->subject, 'Hold cancelled', 'Message has correct title' );
368     is( $message->content, 'Your hold was cancelled.', 'Message has correct content');
369
370     $av->delete;
371     $message->delete;
372 };
373
374 subtest 'Desks' => sub {
375     plan tests => 5;
376     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
377
378     my $desk = Koha::Desk->new({
379         desk_name  => 'my_desk_name_for_test',
380         branchcode => $library->branchcode ,
381                                })->store;
382     ok($desk, "Desk created");
383     my $item = $builder->build_sample_item({ library => $library->branchcode });
384     my $manager = $builder->build_object( { class => "Koha::Patrons" } );
385     t::lib::Mocks::mock_userenv( { patron => $manager, branchcode => $manager->branchcode } );
386
387     my $patron = $builder->build_object(
388         {
389             class => 'Koha::Patrons',
390             value => { branchcode => $library->branchcode, }
391         }
392         );
393
394     my $reserve_id = C4::Reserves::AddReserve(
395         {
396             branchcode     => $library->branchcode,
397             borrowernumber => $patron->borrowernumber,
398             biblionumber   => $item->biblionumber,
399             priority       => 1,
400             itemnumber     => $item->itemnumber,
401         }
402     );
403
404     my $hold = Koha::Holds->find($reserve_id);
405
406     ok($reserve_id, "Hold created");
407     ok($hold, "Hold found");
408     $hold->set_waiting($desk->desk_id);
409     is($hold->found, 'W', 'Hold is waiting with correct status set');
410     is($hold->desk_id, $desk->desk_id, 'Hold is attach to its desk');
411
412 };
413
414 subtest 'get_items_that_can_fill' => sub {
415     plan tests => 6;
416
417     my $biblio = $builder->build_sample_biblio;
418     my $itype_1 = $builder->build_object({ class => 'Koha::ItemTypes' }); # For 1, 2, 3, 4
419     my $itype_2 = $builder->build_object({ class => 'Koha::ItemTypes' });
420     my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype } );
421         # waiting
422     my $item_2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype } );
423     my $item_3 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype } )
424       ;    # onloan
425     my $item_4 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype } )
426       ;    # in transfer
427     my $item_5 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itype => $itype_2->itemtype } );
428     my $lost       = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, itemlost => 1 } );
429     my $withdrawn  = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, withdrawn => 1 } );
430     my $notforloan = $builder->build_sample_item( { biblionumber => $biblio->biblionumber, notforloan => -1 } );
431
432     my $patron_1 = $builder->build_object( { class => 'Koha::Patrons' } );
433     my $patron_2 = $builder->build_object( { class => 'Koha::Patrons' } );
434     my $patron_3 = $builder->build_object( { class => 'Koha::Patrons' } );
435
436     my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
437
438     t::lib::Mocks::mock_userenv( { patron => $patron_1 } );
439
440     my $reserve_id_1 = C4::Reserves::AddReserve(
441         {
442             branchcode     => $library_1->branchcode,
443             borrowernumber => $patron_1->borrowernumber,
444             biblionumber   => $biblio->biblionumber,
445             priority       => 1,
446             itemnumber     => $item_1->itemnumber,
447         }
448     );
449
450     my $holds = Koha::Holds->search({ reserve_id => $reserve_id_1 });
451     my $items = $holds->get_items_that_can_fill;
452     is_deeply( [ map { $_->itemnumber } $items->as_list ], [ $item_1->itemnumber ], 'Item level hold can only be filled by the specific item');
453
454     my $reserve_id_2 = C4::Reserves::AddReserve(
455         {
456             branchcode     => $library_1->branchcode,
457             borrowernumber => $patron_2->borrowernumber,
458             biblionumber   => $biblio->biblionumber,
459             priority       => 2,
460             branchcode     => $item_1->homebranch,
461         }
462     );
463
464     my $waiting_reserve_id = C4::Reserves::AddReserve(
465         {
466             branchcode     => $library_1->branchcode,
467             borrowernumber => $patron_2->borrowernumber,
468             biblionumber   => $biblio->biblionumber,
469             priority       => 0,
470             found          => 'W',
471             itemnumber     => $item_1->itemnumber,
472         }
473     );
474
475     my $notforloan_reserve_id = C4::Reserves::AddReserve(
476         {
477             branchcode     => $library_1->branchcode,
478             borrowernumber => $patron_2->borrowernumber,
479             biblionumber   => $biblio->biblionumber,
480             priority       => 0,
481             itemnumber     => $notforloan->itemnumber,
482         }
483     );
484
485     # item 3 is on loan
486     AddIssue( $patron_3->unblessed, $item_3->barcode );
487
488     # item 4 is in transfer
489     my $from = $builder->build_object( { class => 'Koha::Libraries' } );
490     my $to   = $builder->build_object( { class => 'Koha::Libraries' } );
491     Koha::Item::Transfer->new(
492         {
493             itemnumber  => $item_4->itemnumber,
494             datearrived => undef,
495             frombranch  => $from->branchcode,
496             tobranch    => $to->branchcode
497         }
498     )->store;
499
500     $holds = Koha::Holds->search(
501         {
502             reserve_id => [ $reserve_id_1, $reserve_id_2, $waiting_reserve_id, $notforloan_reserve_id, ]
503         }
504     );
505
506     $items = $holds->get_items_that_can_fill;
507     is_deeply( [ map { $_->itemnumber } $items->as_list ],
508         [ $item_2->itemnumber, $item_5->itemnumber ], 'Only item 2 and 5 are available for filling the hold' );
509
510     # Marking item_5 is no hold allowed
511     Koha::CirculationRule->new(
512         {
513             rule_name  => 'holdallowed',
514             rule_value => 'not_allowed',
515             itemtype   => $item_5->itype
516         }
517     )->store;
518     $items = $holds->get_items_that_can_fill;
519     is_deeply( [ map { $_->itemnumber } $items->as_list ],
520         [ $item_2->itemnumber ], 'Only item 2 is available for filling the hold' );
521
522
523     my $noloan_itype = $builder->build_object( { class => 'Koha::ItemTypes', value => { notforloan => 1 } } );
524     t::lib::Mocks::mock_preference( 'item-level_itypes', 0 );
525     Koha::Holds->find( $waiting_reserve_id )->delete;
526     $holds = Koha::Holds->search(
527         {
528             reserve_id => [ $reserve_id_1, $reserve_id_2 ]
529         }
530     );
531     $items = $holds->get_items_that_can_fill;
532     is_deeply( [ sort map { $_->itemnumber } $items->as_list ],
533         [ $item_1->itemnumber, $item_2->itemnumber, $item_5->itemnumber ], 'Items 1, 2, and 5 are available for filling the holds' );
534
535     my $no_holds = Koha::Holds->new->empty();
536     my $no_items = $no_holds->get_items_that_can_fill();
537     is( ref $no_items, "Koha::Items", "Routine returns a Koha::Items object");
538     is( $no_items->count, 0, "Object is empty when called on no holds");
539
540 };
541
542 subtest 'set_waiting+patron_expiration_date' => sub {
543     plan tests => 2;
544     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
545
546     my $item =
547       $builder->build_sample_item( { library => $library->branchcode } );
548     my $manager = $builder->build_object( { class => "Koha::Patrons" } );
549     t::lib::Mocks::mock_userenv(
550         { patron => $manager, branchcode => $manager->branchcode } );
551
552     my $patron = $builder->build_object(
553         {
554             class => 'Koha::Patrons',
555             value => { branchcode => $library->branchcode, }
556         }
557     );
558
559     subtest 'patron_expiration_date < expiration_date' => sub {
560         plan tests => 6;
561         t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay', 5 );
562         my $patron_expiration_date = dt_from_string->add( days => 3 )->ymd;
563         my $reserve_id             = C4::Reserves::AddReserve(
564             {
565                 branchcode      => $library->branchcode,
566                 borrowernumber  => $patron->borrowernumber,
567                 biblionumber    => $item->biblionumber,
568                 priority        => 1,
569                 itemnumber      => $item->itemnumber,
570                 expiration_date => $patron_expiration_date,
571             }
572         );
573
574         my $hold = Koha::Holds->find($reserve_id);
575
576         is(
577             $hold->expirationdate,
578             $patron_expiration_date,
579             'expiration date set to patron expiration date'
580         );
581         is(
582             $hold->patron_expiration_date, $patron_expiration_date,
583             'patron expiration date correctly set'
584         );
585
586         $hold->set_waiting;
587
588         $hold = $hold->get_from_storage;
589         is( $hold->expirationdate,         $patron_expiration_date );
590         is( $hold->patron_expiration_date, $patron_expiration_date );
591
592         C4::Reserves::RevertWaitingStatus(
593             { itemnumber => $item->itemnumber }
594         );
595
596         $hold = $hold->get_from_storage;
597         is( $hold->expirationdate,         $patron_expiration_date );
598         is( $hold->patron_expiration_date, $patron_expiration_date );
599     };
600
601     subtest 'patron_expiration_date > expiration_date' => sub {
602         plan tests => 6;
603         t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay', 5 );
604         my $new_expiration_date = dt_from_string->add( days => 5 )->ymd;
605         my $patron_expiration_date = dt_from_string->add( days => 6 )->ymd;
606         my $reserve_id             = C4::Reserves::AddReserve(
607             {
608                 branchcode      => $library->branchcode,
609                 borrowernumber  => $patron->borrowernumber,
610                 biblionumber    => $item->biblionumber,
611                 priority        => 1,
612                 itemnumber      => $item->itemnumber,
613                 expiration_date => $patron_expiration_date,
614             }
615         );
616
617         my $hold = Koha::Holds->find($reserve_id);
618
619         is(
620             $hold->expirationdate,
621             $patron_expiration_date,
622             'expiration date set to patron expiration date'
623         );
624         is(
625             $hold->patron_expiration_date, $patron_expiration_date,
626             'patron expiration date correctly set'
627         );
628
629         $hold->set_waiting;
630
631         $hold = $hold->get_from_storage;
632         is( $hold->expirationdate,         $new_expiration_date );
633         is( $hold->patron_expiration_date, $patron_expiration_date );
634
635         C4::Reserves::RevertWaitingStatus(
636             { itemnumber => $item->itemnumber }
637         );
638
639         $hold = $hold->get_from_storage;
640         is( $hold->expirationdate,         $patron_expiration_date );
641         is( $hold->patron_expiration_date, $patron_expiration_date );
642     };
643 };
644
645 subtest 'Test Koha::Hold::item_group' => sub {
646     plan tests => 1;
647     my $library    = $builder->build_object( { class => 'Koha::Libraries' } );
648     my $patron = $builder->build_object({ class => 'Koha::Patrons' });
649     my $item = $builder->build_sample_item;
650     my $item_group = $builder->build_object(
651         {
652             class => 'Koha::Biblio::ItemGroups',
653             value => { biblionumber => $item->biblionumber }
654         }
655     );
656     my $reserve_id = AddReserve(
657         {
658             branchcode       => $library->branchcode,
659             borrowernumber   => $patron->borrowernumber,
660             biblionumber     => $item->biblionumber,
661             itemnumber       => $item->itemnumber,
662             item_group_id    => $item_group->id,
663         }
664     );
665
666     my $hold = Koha::Holds->find($reserve_id);
667     is( $hold->item_group_id, $item_group->id,
668         'Koha::Hold::item_group returns the correct item_group' );
669 };
670
671
672 $schema->storage->txn_rollback;
673
674 subtest 'filter_by_has_cancellation_requests() and filter_out_has_cancellation_requests() tests' => sub {
675
676     plan tests => 7;
677
678     $schema->storage->txn_begin;
679
680     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
681
682     my $item_1 = $builder->build_sample_item;
683     my $item_2 = $builder->build_sample_item;
684     my $item_3 = $builder->build_sample_item;
685
686     my $hold_1 = $builder->build_object(
687         {
688             class => 'Koha::Holds',
689             value => {
690                 found          => 'W',
691                 itemnumber     => $item_1->id,
692                 biblionumber   => $item_1->biblionumber,
693                 borrowernumber => $patron->id
694             }
695         }
696     );
697     my $hold_2 = $builder->build_object(
698         {
699             class => 'Koha::Holds',
700             value => {
701                 found          => 'W',
702                 itemnumber     => $item_2->id,
703                 biblionumber   => $item_2->biblionumber,
704                 borrowernumber => $patron->id
705             }
706         }
707     );
708     my $hold_3 = $builder->build_object(
709         {
710             class => 'Koha::Holds',
711             value => {
712                 found          => 'W',
713                 itemnumber     => $item_3->id,
714                 biblionumber   => $item_3->biblionumber,
715                 borrowernumber => $patron->id
716             }
717         }
718     );
719
720     my $rs = Koha::Holds->search(
721         { reserve_id => [ $hold_1->id, $hold_2->id, $hold_3->id ] } );
722
723     is( $rs->count, 3 );
724
725     my $filtered_rs = $rs->filter_by_has_cancellation_requests;
726
727     is( $filtered_rs->count, 0 );
728
729     my $filtered_out_rs = $rs->filter_out_has_cancellation_requests;
730
731     is( $filtered_out_rs->count, 3 );
732
733     $hold_2->add_cancellation_request;
734
735     $filtered_rs = $rs->filter_by_has_cancellation_requests;
736
737     is( $filtered_rs->count,    1 );
738     is( $filtered_rs->next->id, $hold_2->id );
739
740     $filtered_out_rs = $rs->filter_out_has_cancellation_requests;
741
742     is( $filtered_out_rs->count,    2 );
743     is( $filtered_out_rs->next->id, $hold_1->id );
744
745     $schema->storage->txn_rollback;
746 };