3 # Copyright 2020 Koha Development team
5 # This file is part of Koha
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use Test::More tests => 14;
28 use t::lib::TestBuilder;
32 use Koha::DateUtils qw(dt_from_string);
37 my $schema = Koha::Database->new->schema;
38 my $builder = t::lib::TestBuilder->new;
40 subtest 'store() tests' => sub {
43 $schema->storage->txn_begin;
45 my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
46 my $item = $builder->build_sample_item;
50 borrowernumber => $patron->borrowernumber,
51 biblionumber => $item->biblionumber,
53 itemnumber => $item->itemnumber,
57 'Koha::Exceptions::Hold::MissingPickupLocation',
58 'Exception thrown because branchcode was not passed';
60 my $hold = $builder->build_object( { class => 'Koha::Holds' } );
62 $hold->branchcode(undef)->store;
64 'Koha::Exceptions::Hold::MissingPickupLocation',
65 'Exception thrown if one tries to set branchcode to null';
67 $schema->storage->txn_rollback;
70 subtest 'biblio() tests' => sub {
74 $schema->storage->txn_begin;
76 my $hold = $builder->build_object(
78 class => 'Koha::Holds',
82 local $SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /cannot be null/ };
83 throws_ok { $hold->biblionumber(undef)->store; }
84 'DBIx::Class::Exception',
85 'reserves.biblionumber cannot be null, exception thrown';
87 $schema->storage->txn_rollback;
90 subtest 'pickup_library/branch tests' => sub {
94 $schema->storage->txn_begin;
96 my $hold = $builder->build_object(
98 class => 'Koha::Holds',
102 is( ref( $hold->pickup_library ), 'Koha::Library', '->pickup_library should return a Koha::Library object' );
104 $schema->storage->txn_rollback;
107 subtest 'fill() tests' => sub {
111 $schema->storage->txn_begin;
115 my $category = $builder->build_object(
117 class => 'Koha::Patron::Categories',
118 value => { reservefee => $fee }
121 my $patron = $builder->build_object(
123 class => 'Koha::Patrons',
124 value => { categorycode => $category->id }
127 my $manager = $builder->build_object( { class => 'Koha::Patrons' } );
129 my $title = 'Do what you want';
130 my $biblio = $builder->build_sample_biblio( { title => $title } );
131 my $item = $builder->build_sample_item( { biblionumber => $biblio->id } );
132 my $hold = $builder->build_object(
134 class => 'Koha::Holds',
136 biblionumber => $biblio->id,
137 borrowernumber => $patron->id,
138 itemnumber => $item->id,
144 t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_collected' );
145 t::lib::Mocks::mock_preference( 'HoldsLog', 1 );
146 t::lib::Mocks::mock_userenv(
147 { patron => $manager, branchcode => $manager->branchcode } );
149 my $interface = 'api';
150 C4::Context->interface($interface);
152 my $ret = $hold->fill;
154 is( ref($ret), 'Koha::Hold', '->fill returns the object type' );
155 is( $ret->id, $hold->id, '->fill returns the object' );
157 is( Koha::Holds->find($hold->id), undef, 'Hold no longer current' );
158 my $old_hold = Koha::Old::Holds->find( $hold->id );
160 is( $old_hold->id, $hold->id, 'reserve_id retained' );
161 is( $old_hold->priority, 0, 'priority set to 0' );
162 is( $old_hold->found, 'F', 'found set to F' );
164 subtest 'item_id parameter' => sub {
166 $category->reservefee(0)->store; # do not disturb later accounts
167 $hold = $builder->build_object({ class => 'Koha::Holds', value => { biblionumber => $biblio->id, borrowernumber => $patron->id, itemnumber => undef, priority => 1 } });
168 # Simulating checkout without confirming hold
169 $hold->fill({ item_id => $item->id });
170 $old_hold = Koha::Old::Holds->find($hold->id);
171 is( $old_hold->itemnumber, $item->itemnumber, 'The itemnumber has been saved in old_reserves by fill' );
173 $category->reservefee($fee)->store; # restore
176 subtest 'fee applied tests' => sub {
180 my $account = $patron->account;
181 is( $account->balance, $fee, 'Charge applied correctly' );
183 my $debits = $account->outstanding_debits;
184 is( $debits->count, 1, 'Only one fee charged' );
186 my $fee_debit = $debits->next;
187 is( $fee_debit->amount * 1, $fee, 'Fee amount stored correctly' );
188 is( $fee_debit->description, $title,
189 'Fee description stored correctly' );
190 is( $fee_debit->manager_id, $manager->id,
191 'Fee manager_id stored correctly' );
192 is( $fee_debit->branchcode, $manager->branchcode,
193 'Fee branchcode stored correctly' );
194 is( $fee_debit->interface, $interface,
195 'Fee interface stored correctly' );
196 is( $fee_debit->debit_type_code,
197 'RESERVE', 'Fee debit_type_code stored correctly' );
198 is( $fee_debit->itemnumber, $item->id,
199 'Fee itemnumber stored correctly' );
202 my $logs = Koha::ActionLogs->search(
210 is( $logs->count, 1, '1 log line added' );
212 # Set HoldFeeMode to something other than any_time_is_collected
213 t::lib::Mocks::mock_preference( 'HoldFeeMode', 'not_always' );
215 t::lib::Mocks::mock_preference( 'HoldsLog', 0 );
217 $hold = $builder->build_object(
219 class => 'Koha::Holds',
221 biblionumber => $biblio->id,
222 borrowernumber => $patron->id,
223 itemnumber => $item->id,
231 my $account = $patron->account;
232 is( $account->balance, $fee, 'No new charge applied' );
234 my $debits = $account->outstanding_debits;
235 is( $debits->count, 1, 'Only one fee charged, because of HoldFeeMode' );
237 $logs = Koha::ActionLogs->search(
245 is( $logs->count, 0, 'HoldsLog disabled, no logs added' );
247 subtest 'anonymization behavior tests' => sub {
251 # reduce the tests noise
252 t::lib::Mocks::mock_preference( 'HoldsLog', 0 );
253 t::lib::Mocks::mock_preference( 'HoldFeeMode', 'not_always' );
254 # unset AnonymousPatron
255 t::lib::Mocks::mock_preference( 'AnonymousPatron', undef );
258 $patron->privacy(0)->store;
259 my $hold = $builder->build_object(
261 class => 'Koha::Holds',
262 value => { borrowernumber => $patron->id, found => undef }
266 is( Koha::Old::Holds->find( $hold->id )->borrowernumber,
267 $patron->borrowernumber, 'Patron link is kept' );
269 # 1 == "default", meaning it is not protected from removal
270 $patron->privacy(1)->store;
271 $hold = $builder->build_object(
273 class => 'Koha::Holds',
274 value => { borrowernumber => $patron->id, found => undef }
278 is( Koha::Old::Holds->find( $hold->id )->borrowernumber,
279 $patron->borrowernumber, 'Patron link is kept' );
281 my $anonymous_patron = $builder->build_object({ class => 'Koha::Patrons' });
282 t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous_patron->id );
283 # We need anonymous patron set to change patron privacy to never
284 # (2 == delete immediately)
285 # then we can undef for further tests
286 $patron->privacy(2)->store;
287 t::lib::Mocks::mock_preference( 'AnonymousPatron', undef );
288 $hold = $builder->build_object(
290 class => 'Koha::Holds',
291 value => { borrowernumber => $patron->id, found => undef }
298 'AnonymousPatron not set, exception thrown';
300 $hold->discard_changes; # refresh from DB
302 ok( !$hold->is_found, 'Hold is not filled' );
304 t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous_patron->id );
306 $hold = $builder->build_object(
308 class => 'Koha::Holds',
309 value => { borrowernumber => $patron->id, found => undef }
314 Koha::Old::Holds->find( $hold->id )->borrowernumber,
315 $anonymous_patron->id,
316 'Patron link is set to the configured anonymous patron immediately'
320 subtest 'holds_queue update tests' => sub {
324 my $biblio = $builder->build_sample_biblio;
326 my $mock = Test::MockModule->new('Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue');
327 $mock->mock( 'enqueue', sub {
328 my ( $self, $args ) = @_;
332 '->fill triggers a holds queue update for the related biblio'
336 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 1 );
338 $builder->build_object(
340 class => 'Koha::Holds',
342 biblionumber => $biblio->id,
347 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 0 );
348 # this call shouldn't add a new test
349 $builder->build_object(
351 class => 'Koha::Holds',
353 biblionumber => $biblio->id,
359 $schema->storage->txn_rollback;
362 subtest 'patron() tests' => sub {
366 $schema->storage->txn_begin;
368 my $patron = $builder->build_object({ class => 'Koha::Patrons' });
369 my $hold = $builder->build_object(
371 class => 'Koha::Holds',
373 borrowernumber => $patron->borrowernumber
378 my $hold_patron = $hold->patron;
379 is( ref($hold_patron), 'Koha::Patron', 'Right type' );
380 is( $hold_patron->id, $patron->id, 'Right object' );
382 $schema->storage->txn_rollback;
385 subtest 'set_pickup_location() tests' => sub {
389 $schema->storage->txn_begin;
391 my $mock_biblio = Test::MockModule->new('Koha::Biblio');
392 my $mock_item = Test::MockModule->new('Koha::Item');
394 my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
395 my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
396 my $library_3 = $builder->build_object({ class => 'Koha::Libraries' });
398 # let's control what Koha::Biblio->pickup_locations returns, for testing
399 $mock_biblio->mock( 'pickup_locations', sub {
400 return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
402 # let's mock what Koha::Item->pickup_locations returns, for testing
403 $mock_item->mock( 'pickup_locations', sub {
404 return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
407 my $biblio = $builder->build_sample_biblio;
408 my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
410 # Test biblio-level holds
411 my $biblio_hold = $builder->build_object(
413 class => "Koha::Holds",
415 biblionumber => $biblio->biblionumber,
416 branchcode => $library_3->branchcode,
423 { $biblio_hold->set_pickup_location({ library_id => $library_1->branchcode }); }
424 'Koha::Exceptions::Hold::InvalidPickupLocation',
425 'Exception thrown on invalid pickup location';
427 $biblio_hold->discard_changes;
428 is( $biblio_hold->branchcode, $library_3->branchcode, 'branchcode remains untouched' );
430 my $ret = $biblio_hold->set_pickup_location({ library_id => $library_2->id });
431 is( ref($ret), 'Koha::Hold', 'self is returned' );
433 $biblio_hold->discard_changes;
434 is( $biblio_hold->branchcode, $library_2->id, 'Pickup location changed correctly' );
436 # Test item-level holds
437 my $item_hold = $builder->build_object(
439 class => "Koha::Holds",
441 biblionumber => $biblio->biblionumber,
442 branchcode => $library_3->branchcode,
443 itemnumber => $item->itemnumber,
449 { $item_hold->set_pickup_location({ library_id => $library_1->branchcode }); }
450 'Koha::Exceptions::Hold::InvalidPickupLocation',
451 'Exception thrown on invalid pickup location';
453 $item_hold->discard_changes;
454 is( $item_hold->branchcode, $library_3->branchcode, 'branchcode remains untouched' );
456 $item_hold->set_pickup_location({ library_id => $library_1->branchcode, force => 1 });
457 $item_hold->discard_changes;
458 is( $item_hold->branchcode, $library_1->branchcode, 'branchcode changed because of \'force\'' );
460 $ret = $item_hold->set_pickup_location({ library_id => $library_2->id });
461 is( ref($ret), 'Koha::Hold', 'self is returned' );
463 $item_hold->discard_changes;
464 is( $item_hold->branchcode, $library_2->id, 'Pickup location changed correctly' );
467 { $item_hold->set_pickup_location({ library_id => undef }); }
468 'Koha::Exceptions::MissingParameter',
469 'Exception thrown if missing parameter';
471 like( "$@", qr/The library_id parameter is mandatory/, 'Exception message is clear' );
473 $schema->storage->txn_rollback;
476 subtest 'is_pickup_location_valid() tests' => sub {
480 $schema->storage->txn_begin;
482 my $mock_biblio = Test::MockModule->new('Koha::Biblio');
483 my $mock_item = Test::MockModule->new('Koha::Item');
485 my $library_1 = $builder->build_object({ class => 'Koha::Libraries' });
486 my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
487 my $library_3 = $builder->build_object({ class => 'Koha::Libraries' });
489 # let's control what Koha::Biblio->pickup_locations returns, for testing
490 $mock_biblio->mock( 'pickup_locations', sub {
491 return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
493 # let's mock what Koha::Item->pickup_locations returns, for testing
494 $mock_item->mock( 'pickup_locations', sub {
495 return Koha::Libraries->search( { branchcode => [ $library_2->branchcode, $library_3->branchcode ] } );
498 my $biblio = $builder->build_sample_biblio;
499 my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
501 # Test biblio-level holds
502 my $biblio_hold = $builder->build_object(
504 class => "Koha::Holds",
506 biblionumber => $biblio->biblionumber,
507 branchcode => $library_3->branchcode,
513 ok( !$biblio_hold->is_pickup_location_valid({ library_id => $library_1->branchcode }), 'Pickup location invalid');
514 ok( $biblio_hold->is_pickup_location_valid({ library_id => $library_2->id }), 'Pickup location valid');
516 # Test item-level holds
517 my $item_hold = $builder->build_object(
519 class => "Koha::Holds",
521 biblionumber => $biblio->biblionumber,
522 branchcode => $library_3->branchcode,
523 itemnumber => $item->itemnumber,
528 ok( !$item_hold->is_pickup_location_valid({ library_id => $library_1->branchcode }), 'Pickup location invalid');
529 ok( $item_hold->is_pickup_location_valid({ library_id => $library_2->id }), 'Pickup location valid' );
531 subtest 'pickup_locations() returning ->empty' => sub {
535 $schema->storage->txn_begin;
537 my $library = $builder->build_object({ class => 'Koha::Libraries' });
539 my $mock_item = Test::MockModule->new('Koha::Item');
540 $mock_item->mock( 'pickup_locations', sub { return Koha::Libraries->new->empty; } );
542 my $mock_biblio = Test::MockModule->new('Koha::Biblio');
543 $mock_biblio->mock( 'pickup_locations', sub { return Koha::Libraries->new->empty; } );
545 my $item = $builder->build_sample_item();
546 my $biblio = $item->biblio;
548 # Test biblio-level holds
549 my $biblio_hold = $builder->build_object(
551 class => "Koha::Holds",
553 biblionumber => $biblio->biblionumber,
559 ok( !$biblio_hold->is_pickup_location_valid({ library_id => $library->branchcode }), 'Pickup location invalid');
561 # Test item-level holds
562 my $item_hold = $builder->build_object(
564 class => "Koha::Holds",
566 biblionumber => $biblio->biblionumber,
567 itemnumber => $item->itemnumber,
572 ok( !$item_hold->is_pickup_location_valid({ library_id => $library->branchcode }), 'Pickup location invalid');
574 $schema->storage->txn_rollback;
577 $schema->storage->txn_rollback;
580 subtest 'cancel() tests' => sub {
584 $schema->storage->txn_begin;
586 my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
588 # reduce the tests noise
589 t::lib::Mocks::mock_preference( 'HoldsLog', 0 );
590 t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelayCharge',
593 t::lib::Mocks::mock_preference( 'AnonymousPatron', undef );
596 $patron->privacy(0)->store;
597 my $hold = $builder->build_object(
599 class => 'Koha::Holds',
600 value => { borrowernumber => $patron->id, found => undef }
604 is( Koha::Old::Holds->find( $hold->id )->borrowernumber,
605 $patron->borrowernumber, 'Patron link is kept' );
607 # 1 == "default", meaning it is not protected from removal
608 $patron->privacy(1)->store;
609 $hold = $builder->build_object(
611 class => 'Koha::Holds',
612 value => { borrowernumber => $patron->id, found => undef }
616 is( Koha::Old::Holds->find( $hold->id )->borrowernumber,
617 $patron->borrowernumber, 'Patron link is kept' );
619 my $anonymous_patron = $builder->build_object({ class => 'Koha::Patrons' });
620 t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous_patron->id );
621 # We need anonymous patron set to change patron privacy to never
622 # (2 == delete immediately)
623 # then we can undef for further tests
624 $patron->privacy(2)->store;
625 t::lib::Mocks::mock_preference( 'AnonymousPatron', undef );
626 $hold = $builder->build_object(
628 class => 'Koha::Holds',
629 value => { borrowernumber => $patron->id, found => undef }
635 'AnonymousPatron not set, exception thrown';
637 $hold->discard_changes;
639 ok( !$hold->is_found, 'Hold is not cancelled' );
641 t::lib::Mocks::mock_preference( 'AnonymousPatron', $anonymous_patron->id );
643 $hold = $builder->build_object(
645 class => 'Koha::Holds',
646 value => { borrowernumber => $patron->id, found => undef }
651 Koha::Old::Holds->find( $hold->id )->borrowernumber,
652 $anonymous_patron->id,
653 'Patron link is set to the configured anonymous patron immediately'
656 subtest 'holds_queue update tests' => sub {
660 my $biblio = $builder->build_sample_biblio;
662 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 1 );
664 my $mock = Test::MockModule->new('Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue');
665 $mock->mock( 'enqueue', sub {
666 my ( $self, $args ) = @_;
670 '->cancel triggers a holds queue update for the related biblio'
674 $builder->build_object(
676 class => 'Koha::Holds',
678 biblionumber => $biblio->id,
683 # If the skip_holds_queue param is not honoured, then test count will fail.
684 $builder->build_object(
686 class => 'Koha::Holds',
688 biblionumber => $biblio->id,
691 )->cancel({ skip_holds_queue => 1 });
693 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 0 );
695 $builder->build_object(
697 class => 'Koha::Holds',
699 biblionumber => $biblio->id,
702 )->cancel({ skip_holds_queue => 0 });
705 $schema->storage->txn_rollback;
708 subtest 'suspend_hold() and resume() tests' => sub {
712 $schema->storage->txn_begin;
714 my $biblio = $builder->build_sample_biblio;
717 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 1 );
719 my $mock = Test::MockModule->new('Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue');
720 $mock->mock( 'enqueue', sub {
721 my ( $self, $args ) = @_;
725 "->$action triggers a holds queue update for the related biblio"
729 my $hold = $builder->build_object(
731 class => 'Koha::Holds',
733 biblionumber => $biblio->id,
739 $action = 'suspend_hold';
745 $schema->storage->txn_rollback;
748 subtest 'cancellation_requests(), add_cancellation_request() and cancellation_requested() tests' => sub {
752 $schema->storage->txn_begin;
754 t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 0 );
756 my $hold = $builder->build_object( { class => 'Koha::Holds', } );
758 is( $hold->cancellation_requests->count, 0 );
759 ok( !$hold->cancellation_requested );
761 # Add two cancellation requests
762 my $request_1 = $hold->add_cancellation_request;
763 isnt( $request_1->creation_date, undef, 'creation_date is set' );
765 my $requester = $builder->build_object( { class => 'Koha::Patrons' } );
766 my $creation_date = '2021-06-25 14:05:35';
768 my $request_2 = $hold->add_cancellation_request(
770 creation_date => $creation_date,
774 is( $request_2->creation_date, $creation_date, 'Passed creation_date set' );
776 is( $hold->cancellation_requests->count, 2 );
777 ok( $hold->cancellation_requested );
779 $schema->storage->txn_rollback;
782 subtest 'cancellation_requestable_from_opac() tests' => sub {
786 $schema->storage->txn_begin;
789 $builder->build_object( { class => 'Koha::Patron::Categories' } );
790 my $item_home_library =
791 $builder->build_object( { class => 'Koha::Libraries' } );
792 my $patron_home_library =
793 $builder->build_object( { class => 'Koha::Libraries' } );
796 $builder->build_sample_item( { library => $item_home_library->id } );
797 my $patron = $builder->build_object(
799 class => 'Koha::Patrons',
800 value => { branchcode => $patron_home_library->id }
804 subtest 'Exception cases' => sub {
808 my $hold = $builder->build_object(
810 class => 'Koha::Holds',
814 borrowernumber => $patron->id
819 throws_ok { $hold->cancellation_requestable_from_opac; }
820 'Koha::Exceptions::InvalidStatus',
821 'Exception thrown because hold is not waiting';
823 is( $@->invalid_status, 'hold_not_waiting' );
825 $hold = $builder->build_object(
827 class => 'Koha::Holds',
831 borrowernumber => $patron->id
836 throws_ok { $hold->cancellation_requestable_from_opac; }
837 'Koha::Exceptions::InvalidStatus',
838 'Exception thrown because waiting hold has no item linked';
840 is( $@->invalid_status, 'no_item_linked' );
843 # set default rule to enabled
844 Koha::CirculationRules->set_rule(
849 rule_name => 'waiting_hold_cancellation',
854 my $hold = $builder->build_object(
856 class => 'Koha::Holds',
858 itemnumber => $item->id,
860 borrowernumber => $patron->id
865 t::lib::Mocks::mock_preference( 'ReservesControlBranch',
868 Koha::CirculationRules->set_rule(
870 categorycode => $patron->categorycode,
871 itemtype => $item->itype,
872 branchcode => $item->homebranch,
873 rule_name => 'waiting_hold_cancellation',
878 ok( !$hold->cancellation_requestable_from_opac );
880 Koha::CirculationRules->set_rule(
882 categorycode => $patron->categorycode,
883 itemtype => $item->itype,
884 branchcode => $item->homebranch,
885 rule_name => 'waiting_hold_cancellation',
891 $hold->cancellation_requestable_from_opac,
892 'Make sure it is picking the right circulation rule'
895 t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
897 Koha::CirculationRules->set_rule(
899 categorycode => $patron->categorycode,
900 itemtype => $item->itype,
901 branchcode => $patron->branchcode,
902 rule_name => 'waiting_hold_cancellation',
907 ok( !$hold->cancellation_requestable_from_opac );
909 Koha::CirculationRules->set_rule(
911 categorycode => $patron->categorycode,
912 itemtype => $item->itype,
913 branchcode => $patron->branchcode,
914 rule_name => 'waiting_hold_cancellation',
920 $hold->cancellation_requestable_from_opac,
921 'Make sure it is picking the right circulation rule'
924 $schema->storage->txn_rollback;
927 subtest 'can_update_pickup_location_opac() tests' => sub {
931 $schema->storage->txn_begin;
933 my $hold = $builder->build_object(
934 { class => 'Koha::Holds',
935 value => { found => undef, suspend => 0, suspend_until => undef, waitingdate => undef }
939 t::lib::Mocks::mock_preference( 'OPACAllowUserToChangeBranch', '' );
941 is( $hold->can_update_pickup_location_opac, 0, "Pending hold pickup can't be changed (No change allowed)" );
944 is( $hold->can_update_pickup_location_opac, 0, "In transit hold pickup can't be changed (No change allowed)" );
947 is( $hold->can_update_pickup_location_opac, 0, "Waiting hold pickup can't be changed (No change allowed)" );
950 my $dt = dt_from_string();
952 $hold->suspend_hold( $dt );
953 is( $hold->can_update_pickup_location_opac, 0, "Suspended hold pickup can't be changed (No change allowed)" );
956 t::lib::Mocks::mock_preference( 'OPACAllowUserToChangeBranch', 'pending,intransit,suspended' );
958 is( $hold->can_update_pickup_location_opac, 1, "Pending hold pickup can be changed (pending,intransit,suspended allowed)" );
961 is( $hold->can_update_pickup_location_opac, 1, "In transit hold pickup can be changed (pending,intransit,suspended allowed)" );
964 is( $hold->can_update_pickup_location_opac, 0, "Waiting hold pickup can't be changed (pending,intransit,suspended allowed)" );
967 $dt = dt_from_string();
968 $hold->suspend_hold( $dt );
969 is( $hold->can_update_pickup_location_opac, 1, "Suspended hold pickup can be changed (pending,intransit,suspended allowed)" );
971 $schema->storage->txn_rollback;
974 subtest 'Koha::Hold::item_group tests' => sub {
978 $schema->storage->txn_begin;
980 my $library = $builder->build_object( { class => 'Koha::Libraries' } );
981 my $category = $builder->build_object(
983 class => 'Koha::Patron::Categories',
984 value => { exclude_from_local_holds_priority => 0 }
987 my $patron = $builder->build_object(
989 class => "Koha::Patrons",
991 branchcode => $library->branchcode,
992 categorycode => $category->categorycode
996 my $biblio = $builder->build_sample_biblio();
999 Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id } )->store();
1001 my $hold = $builder->build_object(
1003 class => "Koha::Holds",
1005 borrowernumber => $patron->borrowernumber,
1006 biblionumber => $biblio->biblionumber,
1008 item_group_id => $item_group->id,
1013 is( $hold->item_group->id, $item_group->id, "Got correct item group" );
1015 $schema->storage->txn_rollback;
1018 subtest 'change_type() tests' => sub {
1022 $schema->storage->txn_begin;
1024 my $item = $builder->build_object( { class => 'Koha::Items', } );
1025 my $hold = $builder->build_object(
1027 class => 'Koha::Holds',
1029 itemnumber => undef,
1030 item_level_hold => 0,
1035 my $hold2 = $builder->build_object(
1037 class => 'Koha::Holds',
1039 borrowernumber => $hold->borrowernumber,
1044 ok( $hold->change_type );
1046 $hold->discard_changes;
1048 is( $hold->itemnumber, undef, 'record hold to record hold, no changes' );
1050 is( $hold->item_level_hold, 0, 'item_level_hold=0' );
1052 ok( $hold->change_type( $item->itemnumber ) );
1054 $hold->discard_changes;
1056 is( $hold->itemnumber, $item->itemnumber, 'record hold to item hold' );
1058 is( $hold->item_level_hold, 1, 'item_level_hold=1' );
1060 ok( $hold->change_type( $item->itemnumber ) );
1062 $hold->discard_changes;
1064 is( $hold->itemnumber, $item->itemnumber, 'item hold to item hold, no changes' );
1066 is( $hold->item_level_hold, 1, 'item_level_hold=1' );
1068 ok( $hold->change_type );
1070 $hold->discard_changes;
1072 is( $hold->itemnumber, undef, 'item hold to record hold' );
1074 is( $hold->item_level_hold, 0, 'item_level_hold=0' );
1076 my $hold3 = $builder->build_object(
1078 class => 'Koha::Holds',
1080 biblionumber => $hold->biblionumber,
1081 borrowernumber => $hold->borrowernumber,
1086 throws_ok { $hold->change_type }
1087 'Koha::Exceptions::Hold::CannotChangeHoldType',
1088 'Exception thrown because more than one hold per record';
1090 $schema->storage->txn_rollback;