3 # Copyright 2019 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 => 11;
25 use C4::Biblio qw( GetMarcSubfieldStructure );
26 use C4::Circulation qw( AddIssue AddReturn );
33 use List::MoreUtils qw(all);
35 use t::lib::TestBuilder;
38 my $schema = Koha::Database->new->schema;
39 my $builder = t::lib::TestBuilder->new;
41 subtest 'hidden_in_opac() tests' => sub {
45 $schema->storage->txn_begin;
47 my $item = $builder->build_sample_item({ itemlost => 2 });
50 # disable hidelostitems as it interteres with OpachiddenItems for the calculation
51 t::lib::Mocks::mock_preference( 'hidelostitems', 0 );
53 ok( !$item->hidden_in_opac, 'No rules passed, shouldn\'t hide' );
54 ok( !$item->hidden_in_opac({ rules => $rules }), 'Empty rules passed, shouldn\'t hide' );
56 # enable hidelostitems to verify correct behaviour
57 t::lib::Mocks::mock_preference( 'hidelostitems', 1 );
58 ok( $item->hidden_in_opac, 'Even with no rules, item should hide because of hidelostitems syspref' );
60 # disable hidelostitems
61 t::lib::Mocks::mock_preference( 'hidelostitems', 0 );
62 my $withdrawn = $item->withdrawn + 1; # make sure this attribute doesn't match
64 $rules = { withdrawn => [$withdrawn], itype => [ $item->itype ] };
66 ok( $item->hidden_in_opac({ rules => $rules }), 'Rule matching itype passed, should hide' );
70 $schema->storage->txn_rollback;
73 subtest 'has_pending_hold() tests' => sub {
77 $schema->storage->txn_begin;
79 my $dbh = C4::Context->dbh;
80 my $item = $builder->build_sample_item({ itemlost => 0 });
81 my $itemnumber = $item->itemnumber;
83 $dbh->do("INSERT INTO tmp_holdsqueue (surname,borrowernumber,itemnumber) VALUES ('Clamp',42,$itemnumber)");
84 ok( $item->has_pending_hold, "Yes, we have a pending hold");
85 $dbh->do("DELETE FROM tmp_holdsqueue WHERE itemnumber=$itemnumber");
86 ok( !$item->has_pending_hold, "We don't have a pending hold if nothing in the tmp_holdsqueue");
88 $schema->storage->txn_rollback;
91 subtest "as_marc_field() tests" => sub {
93 my $mss = C4::Biblio::GetMarcSubfieldStructure( '' );
95 my @schema_columns = $schema->resultset('Item')->result_source->columns;
96 my @mapped_columns = grep { exists $mss->{'items.'.$_} } @schema_columns;
98 plan tests => 2 * (scalar @mapped_columns + 1) + 2;
100 $schema->storage->txn_begin;
102 my $item = $builder->build_sample_item;
103 # Make sure it has at least one undefined attribute
104 $item->set({ replacementprice => undef })->store->discard_changes;
106 # Tests with the mss parameter
107 my $marc_field = $item->as_marc_field({ mss => $mss });
111 $mss->{'items.itemnumber'}[0]->{tagfield},
112 'Generated field set the right tag number'
115 foreach my $column ( @mapped_columns ) {
116 my $tagsubfield = $mss->{ 'items.' . $column }[0]->{tagsubfield};
117 is( $marc_field->subfield($tagsubfield),
118 $item->$column, "Value is mapped correctly for column $column" );
121 # Tests without the mss parameter
122 $marc_field = $item->as_marc_field();
126 $mss->{'items.itemnumber'}[0]->{tagfield},
127 'Generated field set the right tag number'
130 foreach my $column (@mapped_columns) {
131 my $tagsubfield = $mss->{ 'items.' . $column }[0]->{tagsubfield};
132 is( $marc_field->subfield($tagsubfield),
133 $item->$column, "Value is mapped correctly for column $column" );
136 my $unmapped_subfield = Koha::MarcSubfieldStructure->new(
139 tagfield => $mss->{'items.itemnumber'}[0]->{tagfield},
144 $mss = C4::Biblio::GetMarcSubfieldStructure( '' );
145 my @unlinked_subfields;
146 push @unlinked_subfields, X => 'Something weird';
147 $item->more_subfields_xml( C4::Items::_get_unlinked_subfields_xml( \@unlinked_subfields ) )->store;
149 $marc_field = $item->as_marc_field;
151 my @subfields = $marc_field->subfields;
152 my $result = all { defined $_->[1] } @subfields;
153 ok( $result, 'There are no undef subfields' );
155 is( scalar $marc_field->subfield('X'), 'Something weird', 'more_subfield_xml is considered' );
157 $schema->storage->txn_rollback;
160 subtest 'pickup_locations' => sub {
163 $schema->storage->txn_begin;
165 my $dbh = C4::Context->dbh;
167 my $root1 = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1, branchcode => undef } } );
168 my $root2 = $builder->build_object( { class => 'Koha::Library::Groups', value => { ft_local_hold_group => 1, branchcode => undef } } );
169 my $library1 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, } } );
170 my $library2 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, } } );
171 my $library3 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 0, } } );
172 my $library4 = $builder->build_object( { class => 'Koha::Libraries', value => { pickup_location => 1, } } );
173 my $group1_1 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root1->id, branchcode => $library1->branchcode } } );
174 my $group1_2 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root1->id, branchcode => $library2->branchcode } } );
176 my $group2_1 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root2->id, branchcode => $library3->branchcode } } );
177 my $group2_2 = $builder->build_object( { class => 'Koha::Library::Groups', value => { parent_id => $root2->id, branchcode => $library4->branchcode } } );
180 $library1->branchcode, $library2->branchcode,
181 $library3->branchcode, $library4->branchcode
184 my $item1 = $builder->build_sample_item(
186 homebranch => $library1->branchcode,
187 holdingbranch => $library2->branchcode,
193 my $item3 = $builder->build_sample_item(
195 homebranch => $library3->branchcode,
196 holdingbranch => $library4->branchcode,
198 itype => $item1->itype,
202 Koha::CirculationRules->set_rules(
204 categorycode => undef,
205 itemtype => $item1->itype,
208 reservesallowed => 25,
214 my $patron1 = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library1->branchcode, firstname => '1' } } );
215 my $patron4 = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $library4->branchcode, firstname => '4' } } );
218 "1-1-from_home_library-any" => 3,
219 "1-1-from_home_library-holdgroup" => 2,
220 "1-1-from_home_library-patrongroup" => 2,
221 "1-1-from_home_library-homebranch" => 1,
222 "1-1-from_home_library-holdingbranch" => 1,
223 "1-1-from_any_library-any" => 3,
224 "1-1-from_any_library-holdgroup" => 2,
225 "1-1-from_any_library-patrongroup" => 2,
226 "1-1-from_any_library-homebranch" => 1,
227 "1-1-from_any_library-holdingbranch" => 1,
228 "1-1-from_local_hold_group-any" => 3,
229 "1-1-from_local_hold_group-holdgroup" => 2,
230 "1-1-from_local_hold_group-patrongroup" => 2,
231 "1-1-from_local_hold_group-homebranch" => 1,
232 "1-1-from_local_hold_group-holdingbranch" => 1,
233 "1-4-from_home_library-any" => 0,
234 "1-4-from_home_library-holdgroup" => 0,
235 "1-4-from_home_library-patrongroup" => 0,
236 "1-4-from_home_library-homebranch" => 0,
237 "1-4-from_home_library-holdingbranch" => 0,
238 "1-4-from_any_library-any" => 3,
239 "1-4-from_any_library-holdgroup" => 2,
240 "1-4-from_any_library-patrongroup" => 1,
241 "1-4-from_any_library-homebranch" => 1,
242 "1-4-from_any_library-holdingbranch" => 1,
243 "1-4-from_local_hold_group-any" => 0,
244 "1-4-from_local_hold_group-holdgroup" => 0,
245 "1-4-from_local_hold_group-patrongroup" => 0,
246 "1-4-from_local_hold_group-homebranch" => 0,
247 "1-4-from_local_hold_group-holdingbranch" => 0,
248 "3-1-from_home_library-any" => 0,
249 "3-1-from_home_library-holdgroup" => 0,
250 "3-1-from_home_library-patrongroup" => 0,
251 "3-1-from_home_library-homebranch" => 0,
252 "3-1-from_home_library-holdingbranch" => 0,
253 "3-1-from_any_library-any" => 3,
254 "3-1-from_any_library-holdgroup" => 1,
255 "3-1-from_any_library-patrongroup" => 2,
256 "3-1-from_any_library-homebranch" => 0,
257 "3-1-from_any_library-holdingbranch" => 1,
258 "3-1-from_local_hold_group-any" => 0,
259 "3-1-from_local_hold_group-holdgroup" => 0,
260 "3-1-from_local_hold_group-patrongroup" => 0,
261 "3-1-from_local_hold_group-homebranch" => 0,
262 "3-1-from_local_hold_group-holdingbranch" => 0,
263 "3-4-from_home_library-any" => 0,
264 "3-4-from_home_library-holdgroup" => 0,
265 "3-4-from_home_library-patrongroup" => 0,
266 "3-4-from_home_library-homebranch" => 0,
267 "3-4-from_home_library-holdingbranch" => 0,
268 "3-4-from_any_library-any" => 3,
269 "3-4-from_any_library-holdgroup" => 1,
270 "3-4-from_any_library-patrongroup" => 1,
271 "3-4-from_any_library-homebranch" => 0,
272 "3-4-from_any_library-holdingbranch" => 1,
273 "3-4-from_local_hold_group-any" => 3,
274 "3-4-from_local_hold_group-holdgroup" => 1,
275 "3-4-from_local_hold_group-patrongroup" => 1,
276 "3-4-from_local_hold_group-homebranch" => 0,
277 "3-4-from_local_hold_group-holdingbranch" => 1
281 my ( $item, $patron, $ha, $hfp, $results ) = @_;
283 Koha::CirculationRules->set_rules(
289 hold_fulfillment_policy => $hfp,
290 returnbranch => 'any'
295 $ha eq 'from_local_hold_group' ? 'holdgroup'
297 $ha eq 'from_any_library' ? 'any'
302 my $pickup_location = $_;
303 grep { $pickup_location->branchcode eq $_ } @branchcodes
304 } $item->pickup_locations( { patron => $patron } )->as_list;
307 scalar(@pl) eq $results->{
308 $item->copynumber . '-'
309 . $patron->firstname . '-'
319 . ', hold_fulfillment_policy: '
323 $item->copynumber . '-'
324 . $patron->firstname . '-'
335 foreach my $item ($item1, $item3) {
336 foreach my $patron ($patron1, $patron4) {
337 #holdallowed 1: homebranch, 2: any, 3: holdgroup
338 foreach my $ha ('from_home_library', 'from_any_library', 'from_local_hold_group') {
339 foreach my $hfp ('any', 'holdgroup', 'patrongroup', 'homebranch', 'holdingbranch') {
340 _doTest($item, $patron, $ha, $hfp, $results);
346 # Now test that branchtransferlimits will further filter the pickup locations
348 my $item_no_ccode = $builder->build_sample_item(
350 homebranch => $library1->branchcode,
351 holdingbranch => $library2->branchcode,
352 itype => $item1->itype,
356 t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
357 t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
358 Koha::CirculationRules->set_rules(
361 itemtype => $item1->itype,
363 holdallowed => 'from_home_library',
364 hold_fulfillment_policy => 1,
365 returnbranch => 'any'
369 $builder->build_object(
371 class => 'Koha::Item::Transfer::Limits',
373 toBranch => $library1->branchcode,
374 fromBranch => $library2->branchcode,
375 itemtype => $item1->itype,
381 my @pickup_locations = map {
382 my $pickup_location = $_;
383 grep { $pickup_location->branchcode eq $_ } @branchcodes
384 } $item1->pickup_locations( { patron => $patron1 } )->as_list;
386 is( scalar @pickup_locations, 3 - 1, "With a transfer limits we get back the libraries that are pickup locations minus 1 limited library");
388 $builder->build_object(
390 class => 'Koha::Item::Transfer::Limits',
392 toBranch => $library4->branchcode,
393 fromBranch => $library2->branchcode,
394 itemtype => $item1->itype,
400 @pickup_locations = map {
401 my $pickup_location = $_;
402 grep { $pickup_location->branchcode eq $_ } @branchcodes
403 } $item1->pickup_locations( { patron => $patron1 } )->as_list;
405 is( scalar @pickup_locations, 3 - 2, "With 2 transfer limits we get back the libraries that are pickup locations minus 2 limited libraries");
407 t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'ccode');
408 @pickup_locations = map {
409 my $pickup_location = $_;
410 grep { $pickup_location->branchcode eq $_ } @branchcodes
411 } $item1->pickup_locations( { patron => $patron1 } )->as_list;
412 is( scalar @pickup_locations, 3, "With no transfer limits of type ccode we get back the libraries that are pickup locations");
414 @pickup_locations = map {
415 my $pickup_location = $_;
416 grep { $pickup_location->branchcode eq $_ } @branchcodes
417 } $item_no_ccode->pickup_locations( { patron => $patron1 } )->as_list;
418 is( scalar @pickup_locations, 3, "With no transfer limits of type ccode and an item with no ccode we get back the libraries that are pickup locations");
420 $builder->build_object(
422 class => 'Koha::Item::Transfer::Limits',
424 toBranch => $library2->branchcode,
425 fromBranch => $library2->branchcode,
427 ccode => $item1->ccode,
432 @pickup_locations = map {
433 my $pickup_location = $_;
434 grep { $pickup_location->branchcode eq $_ } @branchcodes
435 } $item1->pickup_locations( { patron => $patron1 } )->as_list;
436 is( scalar @pickup_locations, 3 - 1, "With a transfer limits we get back the libraries that are pickup locations minus 1 limited library");
438 $builder->build_object(
440 class => 'Koha::Item::Transfer::Limits',
442 toBranch => $library4->branchcode,
443 fromBranch => $library2->branchcode,
445 ccode => $item1->ccode,
450 @pickup_locations = map {
451 my $pickup_location = $_;
452 grep { $pickup_location->branchcode eq $_ } @branchcodes
453 } $item1->pickup_locations( { patron => $patron1 } )->as_list;
454 is( scalar @pickup_locations, 3 - 2, "With 2 transfer limits we get back the libraries that are pickup locations minus 2 limited libraries");
456 t::lib::Mocks::mock_preference('UseBranchTransferLimits', 0);
458 $schema->storage->txn_rollback;
461 subtest 'request_transfer' => sub {
463 $schema->storage->txn_begin;
465 my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
466 my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
467 my $item = $builder->build_sample_item(
469 homebranch => $library1->branchcode,
470 holdingbranch => $library2->branchcode,
474 # Mandatory fields tests
475 throws_ok { $item->request_transfer( { to => $library1 } ) }
476 'Koha::Exceptions::MissingParameter',
477 'Exception thrown if `reason` parameter is missing';
479 throws_ok { $item->request_transfer( { reason => 'Manual' } ) }
480 'Koha::Exceptions::MissingParameter',
481 'Exception thrown if `to` parameter is missing';
484 my $transfer = $item->request_transfer({ to => $library1, reason => 'Manual' });
485 is( ref($transfer), 'Koha::Item::Transfer',
486 'Koha::Item->request_transfer should return a Koha::Item::Transfer object'
488 my $original_transfer = $transfer->get_from_storage;
490 # Transfer already in progress
491 throws_ok { $item->request_transfer( { to => $library2, reason => 'Manual' } ) }
492 'Koha::Exceptions::Item::Transfer::InQueue',
493 'Exception thrown if transfer is already in progress';
496 is( ref( $exception->transfer ),
497 'Koha::Item::Transfer',
498 'The exception contains the found Koha::Item::Transfer' );
501 my $queued_transfer = $item->request_transfer(
502 { to => $library2, reason => 'Manual', enqueue => 1 } );
503 is( ref($queued_transfer), 'Koha::Item::Transfer',
504 'Koha::Item->request_transfer allowed when enqueue is set' );
505 my $transfers = $item->get_transfers;
506 is($transfers->count, 2, "There are now 2 live transfers in the queue");
507 $transfer = $transfer->get_from_storage;
508 is_deeply($transfer->unblessed, $original_transfer->unblessed, "Original transfer unchanged");
509 $queued_transfer->datearrived(dt_from_string)->store();
512 my $replaced_transfer = $item->request_transfer(
513 { to => $library2, reason => 'Manual', replace => 1 } );
514 is( ref($replaced_transfer), 'Koha::Item::Transfer',
515 'Koha::Item->request_transfer allowed when replace is set' );
516 $original_transfer->discard_changes;
517 ok($original_transfer->datecancelled, "Original transfer cancelled");
518 $transfers = $item->get_transfers;
519 is($transfers->count, 1, "There is only 1 live transfer in the queue");
520 $replaced_transfer->datearrived(dt_from_string)->store();
522 # BranchTransferLimits
523 t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
524 t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
525 my $limit = Koha::Item::Transfer::Limit->new({
526 fromBranch => $library2->branchcode,
527 toBranch => $library1->branchcode,
528 itemtype => $item->effective_itemtype,
531 throws_ok { $item->request_transfer( { to => $library1, reason => 'Manual' } ) }
532 'Koha::Exceptions::Item::Transfer::Limit',
533 'Exception thrown if transfer is prevented by limits';
535 my $forced_transfer = $item->request_transfer( { to => $library1, reason => 'Manual', ignore_limits => 1 } );
536 is( ref($forced_transfer), 'Koha::Item::Transfer',
537 'Koha::Item->request_transfer allowed when ignore_limits is set'
540 $schema->storage->txn_rollback;
543 subtest 'deletion' => sub {
546 $schema->storage->txn_begin;
548 my $biblio = $builder->build_sample_biblio();
550 my $item = $builder->build_sample_item(
552 biblionumber => $biblio->biblionumber,
556 is( ref( $item->move_to_deleted ), 'Koha::Schema::Result::Deleteditem', 'Koha::Item->move_to_deleted should return the Deleted item' )
557 ; # FIXME This should be Koha::Deleted::Item
558 is( Koha::Old::Items->search({itemnumber => $item->itemnumber})->count, 1, '->move_to_deleted must have moved the item to deleteditem' );
559 $item = $builder->build_sample_item(
561 biblionumber => $biblio->biblionumber,
565 is( Koha::Old::Items->search({itemnumber => $item->itemnumber})->count, 0, '->move_to_deleted must not have moved the item to deleteditem' );
568 my $library = $builder->build_object({ class => 'Koha::Libraries' });
569 my $library_2 = $builder->build_object({ class => 'Koha::Libraries' });
570 t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode });
572 my $patron = $builder->build_object({class => 'Koha::Patrons'});
573 $item = $builder->build_sample_item({ library => $library->branchcode });
576 C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
579 $item->safe_to_delete,
581 'Koha::Item->safe_to_delete reports item on loan',
587 'item that is on loan cannot be deleted',
590 AddReturn( $item->barcode, $library->branchcode );
592 # book_reserved is tested in t/db_dependent/Reserves.t
595 t::lib::Mocks::mock_preference('IndependentBranches', 1);
596 my $item_2 = $builder->build_sample_item({ library => $library_2->branchcode });
599 $item_2->safe_to_delete,
601 'Koha::Item->safe_to_delete reports IndependentBranches restriction',
605 $item_2->safe_delete,
607 'IndependentBranches prevents deletion at another branch',
612 { # codeblock to limit scope of $module->mock
614 my $module = Test::MockModule->new('C4::Items');
615 $module->mock( GetAnalyticsCount => sub { return 1 } );
617 $item->discard_changes;
619 $item->safe_to_delete,
621 'Koha::Item->safe_to_delete reports linked analytics',
627 'Linked analytics prevents deletion of item',
632 { # last_item_for_hold
633 C4::Reserves::AddReserve({ branchcode => $patron->branchcode, borrowernumber => $patron->borrowernumber, biblionumber => $item->biblionumber });
634 is( $item->safe_to_delete, 'last_item_for_hold', 'Item cannot be deleted if a biblio-level is placed on the biblio and there is only 1 item attached to the biblio' );
636 # With another item attached to the biblio, the item can be deleted
637 $builder->build_sample_item({ biblionumber => $item->biblionumber });
641 $item->safe_to_delete,
643 'Koha::Item->safe_to_delete shows item safe to delete'
648 my $test_item = Koha::Items->find( $item->itemnumber );
650 is( $test_item, undef,
651 "Koha::Item->safe_delete should delete item if safe_to_delete returns true"
654 $schema->storage->txn_rollback;
657 subtest 'renewal_branchcode' => sub {
660 $schema->storage->txn_begin;
662 my $item = $builder->build_sample_item();
663 my $branch = $builder->build_object({ class => 'Koha::Libraries' });
664 my $checkout = $builder->build_object({
665 class => 'Koha::Checkouts',
667 itemnumber => $item->itemnumber,
672 C4::Context->interface( 'intranet' );
673 t::lib::Mocks::mock_userenv({ branchcode => $branch->branchcode });
675 is( $item->renewal_branchcode, $branch->branchcode, "If interface not opac, we get the branch from context");
676 is( $item->renewal_branchcode({ branch => "PANDA"}), $branch->branchcode, "If interface not opac, we get the branch from context even if we pass one in");
677 C4::Context->set_userenv(51, 'userid4tests', undef, 'firstname', 'surname', undef, undef, 0, undef, undef, undef ); #mock userenv doesn't let us set null branch
678 is( $item->renewal_branchcode({ branch => "PANDA"}), "PANDA", "If interface not opac, we get the branch we pass one in if context not set");
680 C4::Context->interface( 'opac' );
682 t::lib::Mocks::mock_preference('OpacRenewalBranch', undef);
683 is( $item->renewal_branchcode, 'OPACRenew', "If interface opac and OpacRenewalBranch undef, we get OPACRenew");
684 is( $item->renewal_branchcode({branch=>'COW'}), 'OPACRenew', "If interface opac and OpacRenewalBranch undef, we get OPACRenew even if branch passed");
686 t::lib::Mocks::mock_preference('OpacRenewalBranch', 'none');
687 is( $item->renewal_branchcode, '', "If interface opac and OpacRenewalBranch is none, we get blank string");
688 is( $item->renewal_branchcode({branch=>'COW'}), '', "If interface opac and OpacRenewalBranch is none, we get blank string even if branch passed");
690 t::lib::Mocks::mock_preference('OpacRenewalBranch', 'checkoutbranch');
691 is( $item->renewal_branchcode, $checkout->branchcode, "If interface opac and OpacRenewalBranch set to checkoutbranch, we get branch of checkout");
692 is( $item->renewal_branchcode({branch=>'MONKEY'}), $checkout->branchcode, "If interface opac and OpacRenewalBranch set to checkoutbranch, we get branch of checkout even if branch passed");
694 t::lib::Mocks::mock_preference('OpacRenewalBranch','patronhomebranch');
695 is( $item->renewal_branchcode, $checkout->patron->branchcode, "If interface opac and OpacRenewalBranch set to patronbranch, we get branch of patron");
696 is( $item->renewal_branchcode({branch=>'TURKEY'}), $checkout->patron->branchcode, "If interface opac and OpacRenewalBranch set to patronbranch, we get branch of patron even if branch passed");
698 t::lib::Mocks::mock_preference('OpacRenewalBranch','itemhomebranch');
699 is( $item->renewal_branchcode, $item->homebranch, "If interface opac and OpacRenewalBranch set to itemhomebranch, we get homebranch of item");
700 is( $item->renewal_branchcode({branch=>'MANATEE'}), $item->homebranch, "If interface opac and OpacRenewalBranch set to itemhomebranch, we get homebranch of item even if branch passed");
702 $schema->storage->txn_rollback;
705 subtest 'Tests for itemtype' => sub {
707 $schema->storage->txn_begin;
709 my $biblio = $builder->build_sample_biblio;
710 my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
711 my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itemtype->itemtype });
713 t::lib::Mocks::mock_preference('item-level_itypes', 1);
714 is( $item->itemtype->itemtype, $item->itype, 'Pref enabled' );
715 t::lib::Mocks::mock_preference('item-level_itypes', 0);
716 is( $item->itemtype->itemtype, $biblio->biblioitem->itemtype, 'Pref disabled' );
718 $schema->storage->txn_rollback;
721 subtest 'get_transfers' => sub {
723 $schema->storage->txn_begin;
725 my $item = $builder->build_sample_item();
727 my $transfers = $item->get_transfers();
728 is(ref($transfers), 'Koha::Item::Transfers', 'Koha::Item->get_transfer should return a Koha::Item::Transfers object' );
729 is($transfers->count, 0, 'When no transfers exist, the Koha::Item:Transfers object should be empty');
731 my $library_to = $builder->build_object( { class => 'Koha::Libraries' } );
733 my $transfer_1 = $builder->build_object(
735 class => 'Koha::Item::Transfers',
737 itemnumber => $item->itemnumber,
738 frombranch => $item->holdingbranch,
739 tobranch => $library_to->branchcode,
742 datearrived => undef,
743 datecancelled => undef,
744 daterequested => \'NOW()'
749 $transfers = $item->get_transfers();
750 is($transfers->count, 1, 'When one transfer has been requested, the Koha::Item:Transfers object should contain one result');
752 my $transfer_2 = $builder->build_object(
754 class => 'Koha::Item::Transfers',
756 itemnumber => $item->itemnumber,
757 frombranch => $item->holdingbranch,
758 tobranch => $library_to->branchcode,
761 datearrived => undef,
762 datecancelled => undef,
763 daterequested => \'NOW()'
768 my $transfer_3 = $builder->build_object(
770 class => 'Koha::Item::Transfers',
772 itemnumber => $item->itemnumber,
773 frombranch => $item->holdingbranch,
774 tobranch => $library_to->branchcode,
777 datearrived => undef,
778 datecancelled => undef,
779 daterequested => \'NOW()'
784 $transfers = $item->get_transfers();
785 is($transfers->count, 3, 'When there are multiple open transfer requests, the Koha::Item::Transfers object contains them all');
786 my $result_1 = $transfers->next;
787 my $result_2 = $transfers->next;
788 my $result_3 = $transfers->next;
789 is( $result_1->branchtransfer_id, $transfer_1->branchtransfer_id, 'Koha::Item->get_transfers returns the oldest transfer request first');
790 is( $result_2->branchtransfer_id, $transfer_2->branchtransfer_id, 'Koha::Item->get_transfers returns the newer transfer request second');
791 is( $result_3->branchtransfer_id, $transfer_3->branchtransfer_id, 'Koha::Item->get_transfers returns the newest transfer request last');
793 $transfer_2->datesent(\'NOW()')->store;
794 $transfers = $item->get_transfers();
795 is($transfers->count, 3, 'When one transfer is set to in_transit, the Koha::Item::Transfers object still contains them all');
796 $result_1 = $transfers->next;
797 $result_2 = $transfers->next;
798 $result_3 = $transfers->next;
799 is( $result_1->branchtransfer_id, $transfer_2->branchtransfer_id, 'Koha::Item->get_transfers returns the active transfer request first');
800 is( $result_2->branchtransfer_id, $transfer_1->branchtransfer_id, 'Koha::Item->get_transfers returns the other transfers oldest to newest');
801 is( $result_3->branchtransfer_id, $transfer_3->branchtransfer_id, 'Koha::Item->get_transfers returns the other transfers oldest to newest');
803 $transfer_2->datearrived(\'NOW()')->store;
804 $transfers = $item->get_transfers();
805 is($transfers->count, 2, 'Once a transfer is received, it no longer appears in the list from ->get_transfers()');
806 $result_1 = $transfers->next;
807 $result_2 = $transfers->next;
808 is( $result_1->branchtransfer_id, $transfer_1->branchtransfer_id, 'Koha::Item->get_transfers returns the other transfers oldest to newest');
809 is( $result_2->branchtransfer_id, $transfer_3->branchtransfer_id, 'Koha::Item->get_transfers returns the other transfers oldest to newest');
811 $transfer_1->datecancelled(\'NOW()')->store;
812 $transfers = $item->get_transfers();
813 is($transfers->count, 1, 'Once a transfer is cancelled, it no longer appears in the list from ->get_transfers()');
814 $result_1 = $transfers->next;
815 is( $result_1->branchtransfer_id, $transfer_3->branchtransfer_id, 'Koha::Item->get_transfers returns the only transfer that remains');
817 $schema->storage->txn_rollback;
820 subtest 'Tests for relationship between item and item_orders via aqorders_item' => sub {
823 $schema->storage->txn_begin;
825 my $biblio = $builder->build_sample_biblio();
826 my $item = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
827 my $aq_budget = $builder->build({
828 source => 'Aqbudget',
830 budget_notes => 'test',
834 my $order_note = 'Order for ' . $item->itemnumber;
836 my $aq_order1 = $builder->build_object({
837 class => 'Koha::Acquisition::Orders',
839 biblionumber => $biblio->biblionumber,
840 budget_id => $aq_budget->{budget_id},
841 order_internalnote => $order_note,
844 my $aq_order2 = $builder->build_object({
845 class => 'Koha::Acquisition::Orders',
847 biblionumber => $biblio->biblionumber,
848 budget_id => $aq_budget->{budget_id},
851 my $aq_order_item1 = $builder->build({
852 source => 'AqordersItem',
854 ordernumber => $aq_order1->ordernumber,
855 itemnumber => $item->itemnumber,
859 my $orders = $item->item_orders;
860 is ($orders->count, 1, 'One order found by item with the relationship');
861 is ($orders->next->order_internalnote, $order_note, 'Correct order found by item with the relationship');
864 subtest 'move_to_biblio() tests' => sub {
867 $schema->storage->txn_begin;
869 my $dbh = C4::Context->dbh;
871 my $source_biblio = $builder->build_sample_biblio();
872 my $target_biblio = $builder->build_sample_biblio();
874 my $source_biblionumber = $source_biblio->biblionumber;
875 my $target_biblionumber = $target_biblio->biblionumber;
877 my $item1 = $builder->build_sample_item({ biblionumber => $source_biblionumber });
878 my $item2 = $builder->build_sample_item({ biblionumber => $source_biblionumber });
879 my $item3 = $builder->build_sample_item({ biblionumber => $source_biblionumber });
881 my $itemnumber1 = $item1->itemnumber;
882 my $itemnumber2 = $item2->itemnumber;
884 my $library = $builder->build_object({ class => 'Koha::Libraries' });
886 my $patron = $builder->build_object({
887 class => 'Koha::Patrons',
888 value => { branchcode => $library->branchcode }
890 my $borrowernumber = $patron->borrowernumber;
892 my $aq_budget = $builder->build({
893 source => 'Aqbudget',
895 budget_notes => 'test',
899 my $aq_order1 = $builder->build_object({
900 class => 'Koha::Acquisition::Orders',
902 biblionumber => $source_biblionumber,
903 budget_id => $aq_budget->{budget_id},
906 my $aq_order_item1 = $builder->build({
907 source => 'AqordersItem',
909 ordernumber => $aq_order1->ordernumber,
910 itemnumber => $itemnumber1,
913 my $aq_order2 = $builder->build_object({
914 class => 'Koha::Acquisition::Orders',
916 biblionumber => $source_biblionumber,
917 budget_id => $aq_budget->{budget_id},
920 my $aq_order_item2 = $builder->build({
921 source => 'AqordersItem',
923 ordernumber => $aq_order2->ordernumber,
924 itemnumber => $itemnumber2,
928 my $bib_level_hold = $builder->build_object({
929 class => 'Koha::Holds',
931 biblionumber => $source_biblionumber,
935 my $item_level_hold1 = $builder->build_object({
936 class => 'Koha::Holds',
938 biblionumber => $source_biblionumber,
939 itemnumber => $itemnumber1,
942 my $item_level_hold2 = $builder->build_object({
943 class => 'Koha::Holds',
945 biblionumber => $source_biblionumber,
946 itemnumber => $itemnumber2,
950 my $tmp_holdsqueue1 = $builder->build({
951 source => 'TmpHoldsqueue',
953 borrowernumber => $borrowernumber,
954 biblionumber => $source_biblionumber,
955 itemnumber => $itemnumber1,
958 my $tmp_holdsqueue2 = $builder->build({
959 source => 'TmpHoldsqueue',
961 borrowernumber => $borrowernumber,
962 biblionumber => $source_biblionumber,
963 itemnumber => $itemnumber2,
966 my $hold_fill_target1 = $builder->build({
967 source => 'HoldFillTarget',
969 borrowernumber => $borrowernumber,
970 biblionumber => $source_biblionumber,
971 itemnumber => $itemnumber1,
974 my $hold_fill_target2 = $builder->build({
975 source => 'HoldFillTarget',
977 borrowernumber => $borrowernumber,
978 biblionumber => $source_biblionumber,
979 itemnumber => $itemnumber2,
982 my $linktracker1 = $builder->build({
983 source => 'Linktracker',
985 borrowernumber => $borrowernumber,
986 biblionumber => $source_biblionumber,
987 itemnumber => $itemnumber1,
990 my $linktracker2 = $builder->build({
991 source => 'Linktracker',
993 borrowernumber => $borrowernumber,
994 biblionumber => $source_biblionumber,
995 itemnumber => $itemnumber2,
999 my $to_biblionumber_after_move = $item1->move_to_biblio($target_biblio);
1000 is($to_biblionumber_after_move, $target_biblionumber, 'move_to_biblio returns the target biblionumber if success');
1002 $to_biblionumber_after_move = $item1->move_to_biblio($target_biblio);
1003 is($to_biblionumber_after_move, undef, 'move_to_biblio returns undef if the move has failed. If called twice, the item is not attached to the first biblio anymore');
1005 my $get_item1 = Koha::Items->find( $item1->itemnumber );
1006 is($get_item1->biblionumber, $target_biblionumber, 'item1 is moved');
1007 my $get_item2 = Koha::Items->find( $item2->itemnumber );
1008 is($get_item2->biblionumber, $source_biblionumber, 'item2 is not moved');
1009 my $get_item3 = Koha::Items->find( $item3->itemnumber );
1010 is($get_item3->biblionumber, $source_biblionumber, 'item3 is not moved');
1012 $aq_order1->discard_changes;
1013 $aq_order2->discard_changes;
1014 is($aq_order1->biblionumber, $target_biblionumber, 'move_to_biblio moves aq_orders for item 1');
1015 is($aq_order2->biblionumber, $source_biblionumber, 'move_to_biblio does not move aq_orders for item 2');
1017 $bib_level_hold->discard_changes;
1018 $item_level_hold1->discard_changes;
1019 $item_level_hold2->discard_changes;
1020 is($bib_level_hold->biblionumber, $source_biblionumber, 'move_to_biblio does not move the biblio-level hold');
1021 is($item_level_hold1->biblionumber, $target_biblionumber, 'move_to_biblio moves the item-level hold placed on item 1');
1022 is($item_level_hold2->biblionumber, $source_biblionumber, 'move_to_biblio does not move the item-level hold placed on item 2');
1024 my $get_tmp_holdsqueue1 = $schema->resultset('TmpHoldsqueue')->search({ itemnumber => $tmp_holdsqueue1->{itemnumber} })->single;
1025 my $get_tmp_holdsqueue2 = $schema->resultset('TmpHoldsqueue')->search({ itemnumber => $tmp_holdsqueue2->{itemnumber} })->single;
1026 is($get_tmp_holdsqueue1->biblionumber, $target_biblionumber, 'move_to_biblio moves tmp_holdsqueue for item 1');
1027 is($get_tmp_holdsqueue2->biblionumber, $source_biblionumber, 'move_to_biblio does not move tmp_holdsqueue for item 2');
1029 my $get_hold_fill_target1 = $schema->resultset('HoldFillTarget')->search({ itemnumber => $hold_fill_target1->{itemnumber} })->single;
1030 my $get_hold_fill_target2 = $schema->resultset('HoldFillTarget')->search({ itemnumber => $hold_fill_target2->{itemnumber} })->single;
1031 # Why does ->biblionumber return a Biblio object???
1032 is($get_hold_fill_target1->biblionumber->biblionumber, $target_biblionumber, 'move_to_biblio moves hold_fill_targets for item 1');
1033 is($get_hold_fill_target2->biblionumber->biblionumber, $source_biblionumber, 'move_to_biblio does not move hold_fill_targets for item 2');
1035 my $get_linktracker1 = $schema->resultset('Linktracker')->search({ itemnumber => $linktracker1->{itemnumber} })->single;
1036 my $get_linktracker2 = $schema->resultset('Linktracker')->search({ itemnumber => $linktracker2->{itemnumber} })->single;
1037 is($get_linktracker1->biblionumber, $target_biblionumber, 'move_to_biblio moves linktracker for item 1');
1038 is($get_linktracker2->biblionumber, $source_biblionumber, 'move_to_biblio does not move linktracker for item 2');
1040 $schema->storage->txn_rollback;