Bug 25827: Add floating toolbar to the holds summary page in staff interface
[koha.git] / t / db_dependent / Items.t
blob010b41e97abb442f90fc6f6bfe9f031dcbbd0ba4
1 #!/usr/bin/perl
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
18 use Modern::Perl;
19 use Data::Dumper;
21 use MARC::Record;
22 use C4::Items;
23 use C4::Biblio;
24 use Koha::Items;
25 use Koha::Database;
26 use Koha::DateUtils qw( dt_from_string );
27 use Koha::Library;
28 use Koha::DateUtils;
29 use Koha::MarcSubfieldStructures;
30 use Koha::Caches;
31 use Koha::AuthorisedValues;
33 use t::lib::Mocks;
34 use t::lib::TestBuilder;
36 use Test::More tests => 15;
38 use Test::Warn;
40 my $schema = Koha::Database->new->schema;
41 my $location = 'My Location';
43 subtest 'General Add, Get and Del tests' => sub {
45 plan tests => 16;
47 $schema->storage->txn_begin;
49 my $builder = t::lib::TestBuilder->new;
50 my $library = $builder->build({
51 source => 'Branch',
52 });
53 my $itemtype = $builder->build({
54 source => 'Itemtype',
55 });
57 # Create a biblio instance for testing
58 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
59 my $biblio = $builder->build_sample_biblio();
61 # Add an item.
62 my $item = $builder->build_sample_item(
64 biblionumber => $biblio->biblionumber,
65 library => $library->{branchcode},
66 location => $location,
67 itype => $itemtype->{itemtype}
70 my $itemnumber = $item->itemnumber;
71 cmp_ok($item->biblionumber, '==', $biblio->biblionumber, "New item is linked to correct biblionumber.");
72 cmp_ok($item->biblioitemnumber, '==', $biblio->biblioitem->biblioitemnumber, "New item is linked to correct biblioitemnumber.");
74 # Get item.
75 my $getitem = Koha::Items->find($itemnumber);
76 cmp_ok($getitem->itemnumber, '==', $itemnumber, "Retrieved item has correct itemnumber.");
77 cmp_ok($getitem->biblioitemnumber, '==', $item->biblioitemnumber, "Retrieved item has correct biblioitemnumber."); # We are not testing anything useful here
78 is( $getitem->location, $location, "The location should not have been modified" );
79 is( $getitem->permanent_location, $location, "The permanent_location should have been set to the location value" );
82 # Do not modify anything, and do not explode!
83 $getitem->set({})->store;
85 # Modify item; setting barcode.
86 $getitem->barcode('987654321')->store;
87 my $moditem = Koha::Items->find($itemnumber);
88 cmp_ok($moditem->barcode, '==', '987654321', 'Modified item barcode successfully to: '.$moditem->barcode . '.');
90 # Delete item.
91 $moditem->delete;
92 my $getdeleted = Koha::Items->find($itemnumber);
93 is($getdeleted, undef, "Item deleted as expected.");
95 $itemnumber = $builder->build_sample_item(
97 biblionumber => $biblio->biblionumber,
98 library => $library->{branchcode},
99 location => $location,
100 permanent_location => 'my permanent location',
101 itype => $itemtype->{itemtype}
103 )->itemnumber;
104 $getitem = Koha::Items->find($itemnumber);
105 is( $getitem->location, $location, "The location should not have been modified" );
106 is( $getitem->permanent_location, 'my permanent location', "The permanent_location should not have modified" );
108 my $new_location = "New location";
109 $getitem->location($new_location)->store;
110 $getitem = Koha::Items->find($itemnumber);
111 is( $getitem->location, $new_location, "The location should have been set to correct location" );
112 is( $getitem->permanent_location, $new_location, "The permanent_location should have been set to location" );
114 $getitem->location('CART')->store;
115 $getitem = Koha::Items->find($itemnumber);
116 is( $getitem->location, 'CART', "The location should have been set to CART" );
117 is( $getitem->permanent_location, $new_location, "The permanent_location should not have been set to CART" );
119 t::lib::Mocks::mock_preference('item-level_itypes', '1');
120 $getitem = Koha::Items->find($itemnumber);
121 is( $getitem->effective_itemtype, $itemtype->{itemtype}, "Itemtype set correctly when using item-level_itypes" );
122 t::lib::Mocks::mock_preference('item-level_itypes', '0');
123 $getitem = Koha::Items->find($itemnumber);
124 is( $getitem->effective_itemtype, $biblio->biblioitem->itemtype, "Itemtype set correctly when not using item-level_itypes" );
126 $schema->storage->txn_rollback;
129 subtest 'ModItem tests' => sub {
130 plan tests => 6;
132 $schema->storage->txn_begin;
134 my $builder = t::lib::TestBuilder->new;
135 my $item = $builder->build_sample_item();
137 my @fields = qw( itemlost withdrawn damaged );
138 for my $field (@fields) {
139 my $field_on = $field."_on";
141 $item->$field(1)->store;
142 $item->discard_changes;
143 is( output_pref({ str => $item->$field_on, dateonly => 1 }), output_pref({ dt => dt_from_string(), dateonly => 1 }), "When updating $field, $field_on is updated" );
145 $item->$field(0)->store;
146 $item->discard_changes;
147 is( $item->$field_on, undef, "When clearing $field, $field_on is cleared" );
150 $schema->storage->txn_rollback;
154 subtest 'ModItemTransfer tests' => sub {
155 plan tests => 8;
157 $schema->storage->txn_begin;
159 my $builder = t::lib::TestBuilder->new;
160 my $item = $builder->build_sample_item();
162 my $library1 = $builder->build(
164 source => 'Branch',
167 my $library2 = $builder->build(
169 source => 'Branch',
173 ModItemTransfer( $item->itemnumber, $library1->{branchcode},
174 $library2->{branchcode} );
176 my $transfers = Koha::Item::Transfers->search(
178 itemnumber => $item->itemnumber
182 is( $transfers->count, 1, "One transfer created with ModItemTransfer" );
183 $item->discard_changes;
184 is($item->holdingbranch, $library1->{branchcode}, "Items holding branch was updated to frombranch");
186 ModItemTransfer( $item->itemnumber, $library2->{branchcode},
187 $library1->{branchcode} );
188 $transfers = Koha::Item::Transfers->search(
189 { itemnumber => $item->itemnumber, },
190 { order_by => { '-asc' => 'branchtransfer_id' } }
193 is($transfers->count, 2, "Second transfer recorded on second call of ModItemTransfer");
194 my $transfer1 = $transfers->next;
195 my $transfer2 = $transfers->next;
196 isnt($transfer1->datearrived, undef, "First transfer marked as completed by ModItemTransfer");
197 like($transfer1->comments,qr/^Canceled/, "First transfer contains 'Canceled' comment");
198 is($transfer2->datearrived, undef, "Second transfer is now the active transfer");
199 $item->discard_changes;
200 is($item->holdingbranch, $library2->{branchcode}, "Items holding branch was updated to frombranch");
202 # Check 'reason' is populated when passed
203 ModItemTransfer( $item->itemnumber, $library2->{branchcode},
204 $library1->{branchcode}, "Manual" );
206 $transfers = Koha::Item::Transfers->search(
207 { itemnumber => $item->itemnumber, },
208 { order_by => { '-desc' => 'branchtransfer_id' } }
211 my $transfer3 = $transfers->next;
212 is($transfer3->reason, 'Manual', "Reason set via ModItemTransfer");
214 $schema->storage->txn_rollback;
217 subtest 'GetHiddenItemnumbers tests' => sub {
219 plan tests => 11;
221 # This sub is controlled by the OpacHiddenItems system preference.
223 $schema->storage->txn_begin;
225 my $builder = t::lib::TestBuilder->new;
226 my $library1 = $builder->build({
227 source => 'Branch',
230 my $library2 = $builder->build({
231 source => 'Branch',
233 my $itemtype = $builder->build({
234 source => 'Itemtype',
237 # Create a new biblio
238 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
239 my $biblio = $builder->build_sample_biblio();
241 # Add two items
242 my $item1_itemnumber = $builder->build_sample_item(
244 biblionumber => $biblio->biblionumber,
245 library => $library1->{branchcode},
246 withdrawn => 1,
247 itype => $itemtype->{itemtype}
249 )->itemnumber;
250 my $item2_itemnumber = $builder->build_sample_item(
252 biblionumber => $biblio->biblionumber,
253 library => $library2->{branchcode},
254 withdrawn => 0,
255 itype => $itemtype->{itemtype}
257 )->itemnumber;
258 my $opachiddenitems;
259 my @itemnumbers = ($item1_itemnumber,$item2_itemnumber);
260 my @hidden;
261 my @items;
262 push @items, Koha::Items->find( $item1_itemnumber )->unblessed;
263 push @items, Koha::Items->find( $item2_itemnumber )->unblessed;
265 # Empty OpacHiddenItems
266 t::lib::Mocks::mock_preference('OpacHiddenItems','');
267 ok( !defined( GetHiddenItemnumbers( { items => \@items } ) ),
268 "Hidden items list undef if OpacHiddenItems empty");
270 # Blank spaces
271 t::lib::Mocks::mock_preference('OpacHiddenItems',' ');
272 ok( scalar GetHiddenItemnumbers( { items => \@items } ) == 0,
273 "Hidden items list empty if OpacHiddenItems only contains blanks");
275 # One variable / value
276 $opachiddenitems = "
277 withdrawn: [1]";
278 t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
279 @hidden = GetHiddenItemnumbers( { items => \@items } );
280 ok( scalar @hidden == 1, "Only one hidden item");
281 is( $hidden[0], $item1_itemnumber, "withdrawn=1 is hidden");
283 # One variable, two values
284 $opachiddenitems = "
285 withdrawn: [1,0]";
286 t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
287 @hidden = GetHiddenItemnumbers( { items => \@items } );
288 ok( scalar @hidden == 2, "Two items hidden");
289 is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and withdrawn=0 hidden");
291 # Two variables, a value each
292 $opachiddenitems = "
293 withdrawn: [1]
294 homebranch: [$library2->{branchcode}]
296 t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
297 @hidden = GetHiddenItemnumbers( { items => \@items } );
298 ok( scalar @hidden == 2, "Two items hidden");
299 is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and homebranch library2 hidden");
301 # Override hidden with patron category
302 t::lib::Mocks::mock_preference( 'OpacHiddenItemsExceptions', 'S' );
303 @hidden = GetHiddenItemnumbers( { items => \@items, borcat => 'PT' } );
304 ok( scalar @hidden == 2, "Two items still hidden");
305 @hidden = GetHiddenItemnumbers( { items => \@items, borcat => 'S' } );
306 ok( scalar @hidden == 0, "Two items not hidden");
308 # Valid OpacHiddenItems, empty list
309 @items = ();
310 @hidden = GetHiddenItemnumbers( { items => \@items } );
311 ok( scalar @hidden == 0, "Empty items list, no item hidden");
313 $schema->storage->txn_rollback;
316 subtest 'GetItemsInfo tests' => sub {
318 plan tests => 9;
320 $schema->storage->txn_begin;
322 my $builder = t::lib::TestBuilder->new;
323 my $library1 = $builder->build({
324 source => 'Branch',
326 my $library2 = $builder->build({
327 source => 'Branch',
329 my $itemtype = $builder->build({
330 source => 'Itemtype',
333 Koha::AuthorisedValues->delete;
334 my $av1 = Koha::AuthorisedValue->new(
336 category => 'RESTRICTED',
337 authorised_value => '1',
338 lib => 'Restricted Access',
339 lib_opac => 'Restricted Access OPAC',
341 )->store();
343 # Add a biblio
344 my $biblio = $builder->build_sample_biblio();
345 # Add an item
346 my $itemnumber = $builder->build_sample_item(
348 biblionumber => $biblio->biblionumber,
349 homebranch => $library1->{branchcode},
350 holdingbranch => $library2->{branchcode},
351 itype => $itemtype->{itemtype},
352 restricted => 1,
354 )->itemnumber;
356 my $library = Koha::Libraries->find( $library1->{branchcode} );
357 $library->opac_info("homebranch OPAC info");
358 $library->store;
360 $library = Koha::Libraries->find( $library2->{branchcode} );
361 $library->opac_info("holdingbranch OPAC info");
362 $library->store;
364 my @results = GetItemsInfo( $biblio->biblionumber );
365 ok( @results, 'GetItemsInfo returns results');
367 is( $results[0]->{ home_branch_opac_info }, "homebranch OPAC info",
368 'GetItemsInfo returns the correct home branch OPAC info notice' );
369 is( $results[0]->{ holding_branch_opac_info }, "holdingbranch OPAC info",
370 'GetItemsInfo returns the correct holding branch OPAC info notice' );
371 is( exists( $results[0]->{ onsite_checkout } ), 1,
372 'GetItemsInfo returns a onsite_checkout key' );
373 is( $results[0]->{ restricted }, 1,
374 'GetItemsInfo returns a restricted value code' );
375 is( $results[0]->{ restrictedvalue }, "Restricted Access",
376 'GetItemsInfo returns a restricted value description (staff)' );
377 is( $results[0]->{ restrictedvalueopac }, "Restricted Access OPAC",
378 'GetItemsInfo returns a restricted value description (OPAC)' );
380 #place item into holds queue
381 my $dbh = C4::Context->dbh;
382 @results = GetItemsInfo( $biblio->biblionumber );
383 is( $results[0]->{ has_pending_hold }, "0",
384 'Hold not marked as pending/unavailable if nothing in tmp_holdsqueue for item' );
386 $dbh->do(q{INSERT INTO tmp_holdsqueue (biblionumber, itemnumber, surname, borrowernumber ) VALUES (?, ?, "Zorro", 42)}, undef, $biblio->biblionumber, $itemnumber);
387 @results = GetItemsInfo( $biblio->biblionumber );
388 is( $results[0]->{ has_pending_hold }, "1",
389 'Hold marked as pending/unavailable if tmp_holdsqueue is not empty for item' );
391 $schema->storage->txn_rollback;
394 subtest q{Test Koha::Database->schema()->resultset('Item')->itemtype()} => sub {
396 plan tests => 4;
398 $schema->storage->txn_begin;
400 my $biblio = $schema->resultset('Biblio')->create({
401 title => "Test title",
402 datecreated => dt_from_string,
403 biblioitems => [ { itemtype => 'BIB_LEVEL' } ],
405 my $biblioitem = $biblio->biblioitems->first;
406 my $item = $schema->resultset('Item')->create({
407 biblioitemnumber => $biblioitem->biblioitemnumber,
408 biblionumber => $biblio->biblionumber,
409 itype => "ITEM_LEVEL",
412 t::lib::Mocks::mock_preference( 'item-level_itypes', 0 );
413 is( $item->effective_itemtype(), 'BIB_LEVEL', '$item->itemtype() returns biblioitem.itemtype when item-level_itypes is disabled' );
415 t::lib::Mocks::mock_preference( 'item-level_itypes', 1 );
416 is( $item->effective_itemtype(), 'ITEM_LEVEL', '$item->itemtype() returns items.itype when item-level_itypes is enabled' );
418 # If itemtype is not defined and item-level_level item types are set
419 # fallback to biblio-level itemtype (Bug 14651) and warn
420 $item->itype( undef );
421 $item->update();
422 my $effective_itemtype;
423 warning_is { $effective_itemtype = $item->effective_itemtype() }
424 "item-level_itypes set but no itemtype set for item (".$item->itemnumber.")",
425 '->effective_itemtype() raises a warning when falling back to bib-level';
427 ok( defined $effective_itemtype &&
428 $effective_itemtype eq 'BIB_LEVEL',
429 '$item->effective_itemtype() falls back to biblioitems.itemtype when item-level_itypes is enabled but undef' );
431 $schema->storage->txn_rollback;
434 subtest 'SearchItems test' => sub {
435 plan tests => 15;
437 $schema->storage->txn_begin;
438 my $dbh = C4::Context->dbh;
439 my $builder = t::lib::TestBuilder->new;
441 my $library1 = $builder->build({
442 source => 'Branch',
444 my $library2 = $builder->build({
445 source => 'Branch',
447 my $itemtype = $builder->build({
448 source => 'Itemtype',
451 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
452 my ($cpl_items_before) = SearchItems( { field => 'homebranch', query => $library1->{branchcode} } );
454 my $biblio = $builder->build_sample_biblio({ title => 'Silence in the library' });
455 $builder->build_sample_biblio({ title => 'Silence in the shadow' });
457 my (undef, $initial_items_count) = SearchItems(undef, {rows => 1});
459 # Add two items
460 my $item1_itemnumber = $builder->build_sample_item(
462 biblionumber => $biblio->biblionumber,
463 library => $library1->{branchcode},
464 itype => $itemtype->{itemtype}
466 )->itemnumber;
467 my $item2_itemnumber = $builder->build_sample_item(
469 biblionumber => $biblio->biblionumber,
470 library => $library2->{branchcode},
471 itype => $itemtype->{itemtype}
473 )->itemnumber;
475 my ($items, $total_results);
477 ($items, $total_results) = SearchItems();
478 is($total_results, $initial_items_count + 2, "Created 2 new items");
479 is(scalar @$items, $total_results, "SearchItems() returns all items");
481 ($items, $total_results) = SearchItems(undef, {rows => 1});
482 is($total_results, $initial_items_count + 2);
483 is(scalar @$items, 1, "SearchItems(undef, {rows => 1}) returns only 1 item");
485 # Search all items where homebranch = 'CPL'
486 my $filter = {
487 field => 'homebranch',
488 query => $library1->{branchcode},
489 operator => '=',
491 ($items, $total_results) = SearchItems($filter);
492 ok($total_results > 0, "There is at least one CPL item");
493 my $all_items_are_CPL = 1;
494 foreach my $item (@$items) {
495 if ($item->{homebranch} ne $library1->{branchcode}) {
496 $all_items_are_CPL = 0;
497 last;
500 ok($all_items_are_CPL, "All items returned by SearchItems are from CPL");
502 # Search all items where homebranch != 'CPL'
503 $filter = {
504 field => 'homebranch',
505 query => $library1->{branchcode},
506 operator => '!=',
508 ($items, $total_results) = SearchItems($filter);
509 ok($total_results > 0, "There is at least one non-CPL item");
510 my $all_items_are_not_CPL = 1;
511 foreach my $item (@$items) {
512 if ($item->{homebranch} eq $library1->{branchcode}) {
513 $all_items_are_not_CPL = 0;
514 last;
517 ok($all_items_are_not_CPL, "All items returned by SearchItems are not from CPL");
519 # Search all items where biblio title (245$a) is like 'Silence in the %'
520 $filter = {
521 field => 'marc:245$a',
522 query => 'Silence in the %',
523 operator => 'like',
525 ($items, $total_results) = SearchItems($filter);
526 ok($total_results >= 2, "There is at least 2 items with a biblio title like 'Silence in the %'");
528 # Search all items where biblio title is 'Silence in the library'
529 # and homebranch is 'CPL'
530 $filter = {
531 conjunction => 'AND',
532 filters => [
534 field => 'marc:245$a',
535 query => 'Silence in the %',
536 operator => 'like',
539 field => 'homebranch',
540 query => $library1->{branchcode},
541 operator => '=',
545 ($items, $total_results) = SearchItems($filter);
546 my $found = 0;
547 foreach my $item (@$items) {
548 if ($item->{itemnumber} == $item1_itemnumber) {
549 $found = 1;
550 last;
553 ok($found, "item1 found");
555 my $frameworkcode = q||;
556 my ($itemfield) = GetMarcFromKohaField( 'items.itemnumber' );
558 # Create item subfield 'z' without link
559 $dbh->do('DELETE FROM marc_subfield_structure WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
560 $dbh->do('INSERT INTO marc_subfield_structure (tagfield, tagsubfield, frameworkcode) VALUES (?, "z", ?)', undef, $itemfield, $frameworkcode);
562 # Clear cache
563 my $cache = Koha::Caches->get_instance();
564 $cache->clear_from_cache("MarcStructure-0-$frameworkcode");
565 $cache->clear_from_cache("MarcStructure-1-$frameworkcode");
566 $cache->clear_from_cache("default_value_for_mod_marc-");
567 $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
569 my $item3_record = new MARC::Record;
570 $item3_record->append_fields(
571 new MARC::Field(
572 $itemfield, '', '',
573 'z' => 'foobar',
574 'y' => $itemtype->{itemtype}
577 my (undef, undef, $item3_itemnumber) = AddItemFromMarc($item3_record,
578 $biblio->biblionumber);
580 # Search item where item subfield z is "foobar"
581 $filter = {
582 field => 'marc:' . $itemfield . '$z',
583 query => 'foobar',
584 operator => 'like',
586 ($items, $total_results) = SearchItems($filter);
587 ok(scalar @$items == 1, 'found 1 item with $z = "foobar"');
589 # Link $z to items.itemnotes (and make sure there is no other subfields
590 # linked to it)
591 $dbh->do('DELETE FROM marc_subfield_structure WHERE kohafield="items.itemnotes" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
592 $dbh->do('UPDATE marc_subfield_structure SET kohafield="items.itemnotes" WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
594 # Clear cache
595 $cache->clear_from_cache("MarcStructure-0-$frameworkcode");
596 $cache->clear_from_cache("MarcStructure-1-$frameworkcode");
597 $cache->clear_from_cache("default_value_for_mod_marc-");
598 $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
600 ModItemFromMarc($item3_record, $biblio->biblionumber, $item3_itemnumber);
602 # Make sure the link is used
603 my $item3 = Koha::Items->find($item3_itemnumber);
604 is($item3->itemnotes, 'foobar', 'itemnotes eq "foobar"');
606 # Do the same search again.
607 # This time it will search in items.itemnotes
608 ($items, $total_results) = SearchItems($filter);
609 is(scalar(@$items), 1, 'found 1 item with itemnotes = "foobar"');
611 my ($cpl_items_after) = SearchItems( { field => 'homebranch', query => $library1->{branchcode} } );
612 is( ( scalar( @$cpl_items_after ) - scalar ( @$cpl_items_before ) ), 1, 'SearchItems should return something' );
614 # Issues count = 0
615 $filter = {
616 conjunction => 'AND',
617 filters => [
619 field => 'issues',
620 query => 0,
621 operator => '=',
624 field => 'homebranch',
625 query => $library1->{branchcode},
626 operator => '=',
630 ($items, $total_results) = SearchItems($filter);
631 is($total_results, 1, "Search items.issues issues = 0 returns result (items.issues defaults to 0)");
633 $schema->storage->txn_rollback;
636 subtest 'Koha::Item(s) tests' => sub {
638 plan tests => 7;
640 $schema->storage->txn_begin();
642 my $builder = t::lib::TestBuilder->new;
643 my $library1 = $builder->build({
644 source => 'Branch',
646 my $library2 = $builder->build({
647 source => 'Branch',
649 my $itemtype = $builder->build({
650 source => 'Itemtype',
653 # Create a biblio and item for testing
654 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
655 my $biblio = $builder->build_sample_biblio();
656 my $itemnumber = $builder->build_sample_item(
658 biblionumber => $biblio->biblionumber,
659 homebranch => $library1->{branchcode},
660 holdingbranch => $library2->{branchcode},
661 itype => $itemtype->{itemtype}
663 )->itemnumber;
665 # Get item.
666 my $item = Koha::Items->find( $itemnumber );
667 is( ref($item), 'Koha::Item', "Got Koha::Item" );
669 my $homebranch = $item->home_branch();
670 is( ref($homebranch), 'Koha::Library', "Got Koha::Library from home_branch method" );
671 is( $homebranch->branchcode(), $library1->{branchcode}, "Home branch code matches homebranch" );
673 my $holdingbranch = $item->holding_branch();
674 is( ref($holdingbranch), 'Koha::Library', "Got Koha::Library from holding_branch method" );
675 is( $holdingbranch->branchcode(), $library2->{branchcode}, "Home branch code matches holdingbranch" );
677 $biblio = $item->biblio();
678 is( ref($item->biblio), 'Koha::Biblio', "Got Koha::Biblio from biblio method" );
679 is( $item->biblio->title(), $biblio->title, 'Title matches biblio title' );
681 $schema->storage->txn_rollback;
684 subtest 'C4::Biblio::EmbedItemsInMarcBiblio' => sub {
685 plan tests => 8;
687 $schema->storage->txn_begin();
689 my $builder = t::lib::TestBuilder->new;
690 my $library1 = $builder->build({
691 source => 'Branch',
693 my $library2 = $builder->build({
694 source => 'Branch',
696 my $itemtype = $builder->build({
697 source => 'Itemtype',
700 my $biblio = $builder->build_sample_biblio();
701 my $item_infos = [
702 { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
703 { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
704 { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
705 { homebranch => $library2->{branchcode}, holdingbranch => $library2->{branchcode} },
706 { homebranch => $library2->{branchcode}, holdingbranch => $library2->{branchcode} },
707 { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
708 { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
709 { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
711 my $number_of_items = scalar @$item_infos;
712 my $number_of_items_with_homebranch_is_CPL =
713 grep { $_->{homebranch} eq $library1->{branchcode} } @$item_infos;
715 my @itemnumbers;
716 for my $item_info (@$item_infos) {
717 my $itemnumber = $builder->build_sample_item(
719 biblionumber => $biblio->biblionumber,
720 homebranch => $item_info->{homebranch},
721 holdingbranch => $item_info->{holdingbranch},
722 itype => $itemtype->{itemtype}
724 )->itemnumber;
726 push @itemnumbers, $itemnumber;
729 # Emptied the OpacHiddenItems pref
730 t::lib::Mocks::mock_preference( 'OpacHiddenItems', '' );
732 my ($itemfield) =
733 C4::Biblio::GetMarcFromKohaField( 'items.itemnumber' );
734 my $record = C4::Biblio::GetMarcBiblio({ biblionumber => $biblio->biblionumber });
735 warning_is { C4::Biblio::EmbedItemsInMarcBiblio() }
736 { carped => 'EmbedItemsInMarcBiblio: No MARC record passed' },
737 'Should carp is no record passed.';
739 C4::Biblio::EmbedItemsInMarcBiblio({
740 marc_record => $record,
741 biblionumber => $biblio->biblionumber });
742 my @items = $record->field($itemfield);
743 is( scalar @items, $number_of_items, 'Should return all items' );
745 my $marc_with_items = C4::Biblio::GetMarcBiblio({
746 biblionumber => $biblio->biblionumber,
747 embed_items => 1 });
748 is_deeply( $record, $marc_with_items, 'A direct call to GetMarcBiblio with items matches');
750 C4::Biblio::EmbedItemsInMarcBiblio({
751 marc_record => $record,
752 biblionumber => $biblio->biblionumber,
753 item_numbers => [ $itemnumbers[1], $itemnumbers[3] ] });
754 @items = $record->field($itemfield);
755 is( scalar @items, 2, 'Should return all items present in the list' );
757 C4::Biblio::EmbedItemsInMarcBiblio({
758 marc_record => $record,
759 biblionumber => $biblio->biblionumber,
760 opac => 1 });
761 @items = $record->field($itemfield);
762 is( scalar @items, $number_of_items, 'Should return all items for opac' );
764 my $opachiddenitems = "
765 homebranch: ['$library1->{branchcode}']";
766 t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
768 C4::Biblio::EmbedItemsInMarcBiblio({
769 marc_record => $record,
770 biblionumber => $biblio->biblionumber });
771 @items = $record->field($itemfield);
772 is( scalar @items,
773 $number_of_items,
774 'Even with OpacHiddenItems set, all items should have been embedded' );
776 C4::Biblio::EmbedItemsInMarcBiblio({
777 marc_record => $record,
778 biblionumber => $biblio->biblionumber,
779 opac => 1 });
780 @items = $record->field($itemfield);
782 scalar @items,
783 $number_of_items - $number_of_items_with_homebranch_is_CPL,
784 'For OPAC, the pref OpacHiddenItems should have been take into account. Only items with homebranch ne CPL should have been embedded'
787 $opachiddenitems = "
788 homebranch: ['$library1->{branchcode}', '$library2->{branchcode}']";
789 t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
790 C4::Biblio::EmbedItemsInMarcBiblio({
791 marc_record => $record,
792 biblionumber => $biblio->biblionumber,
793 opac => 1 });
794 @items = $record->field($itemfield);
796 scalar @items,
798 'For OPAC, If all items are hidden, no item should have been embedded'
801 $schema->storage->txn_rollback;
805 subtest 'get_hostitemnumbers_of' => sub {
806 plan tests => 3;
808 $schema->storage->txn_begin;
809 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
810 my $builder = t::lib::TestBuilder->new;
812 # Host item field without 0 or 9
813 my $bib1 = MARC::Record->new();
814 $bib1->append_fields(
815 MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
816 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
817 MARC::Field->new('773', ' ', ' ', b => 'b without 0 or 9'),
819 my ($biblionumber1, $bibitemnum1) = AddBiblio($bib1, '');
820 my @itemnumbers1 = C4::Items::get_hostitemnumbers_of( $biblionumber1 );
821 is( scalar @itemnumbers1, 0, '773 without 0 or 9');
823 # Correct host item field, analytical records on
824 t::lib::Mocks::mock_preference('EasyAnalyticalRecords', 1);
825 my $hostitem = $builder->build_sample_item();
826 my $bib2 = MARC::Record->new();
827 $bib2->append_fields(
828 MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
829 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
830 MARC::Field->new('773', ' ', ' ', 0 => $hostitem->biblionumber , 9 => $hostitem->itemnumber, b => 'b' ),
832 my ($biblionumber2, $bibitemnum2) = AddBiblio($bib2, '');
833 my @itemnumbers2 = C4::Items::get_hostitemnumbers_of( $biblionumber2 );
834 is( scalar @itemnumbers2, 1, '773 with 0 and 9, EasyAnalyticalRecords on');
836 # Correct host item field, analytical records off
837 t::lib::Mocks::mock_preference('EasyAnalyticalRecords', 0);
838 @itemnumbers2 = C4::Items::get_hostitemnumbers_of( $biblionumber2 );
839 is( scalar @itemnumbers2, 0, '773 with 0 and 9, EasyAnalyticalRecords off');
841 $schema->storage->txn_rollback;
844 subtest 'Test logging for ModItem' => sub {
846 plan tests => 3;
848 t::lib::Mocks::mock_preference('CataloguingLog', 1);
850 $schema->storage->txn_begin;
852 my $builder = t::lib::TestBuilder->new;
853 my $library = $builder->build({
854 source => 'Branch',
856 my $itemtype = $builder->build({
857 source => 'Itemtype',
860 # Create a biblio instance for testing
861 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
862 my $biblio = $builder->build_sample_biblio();
864 # Add an item.
865 my $item = $builder->build_sample_item(
867 biblionumber => $biblio->biblionumber,
868 library => $library->{homebranch},
869 location => $location,
870 itype => $itemtype->{itemtype}
874 # False means no logging
875 $schema->resultset('ActionLog')->search()->delete();
876 $item->location($location)->store({ log_action => 0 });
877 is( $schema->resultset('ActionLog')->count(), 0, 'False value does not trigger logging' );
879 # True means logging
880 $schema->resultset('ActionLog')->search()->delete();
881 $item->location('new location')->store({ log_action => 1 });
882 is( $schema->resultset('ActionLog')->count(), 1, 'True value does trigger logging' );
884 # Undefined defaults to true
885 $schema->resultset('ActionLog')->search()->delete();
886 $item->location($location)->store();
887 is( $schema->resultset('ActionLog')->count(), 1, 'Undefined value defaults to true, triggers logging' );
889 $schema->storage->txn_rollback;
892 subtest 'Check stockrotationitem relationship' => sub {
893 plan tests => 1;
895 $schema->storage->txn_begin();
897 my $builder = t::lib::TestBuilder->new;
898 my $item = $builder->build({ source => 'Item' });
900 $builder->build({
901 source => 'Stockrotationitem',
902 value => { itemnumber_id => $item->{itemnumber} }
905 my $sritem = Koha::Items->find($item->{itemnumber})->stockrotationitem;
906 isa_ok( $sritem, 'Koha::StockRotationItem', "Relationship works and correctly creates Koha::Object." );
908 $schema->storage->txn_rollback;
911 subtest 'Check add_to_rota method' => sub {
912 plan tests => 2;
914 $schema->storage->txn_begin();
916 my $builder = t::lib::TestBuilder->new;
917 my $item = $builder->build({ source => 'Item' });
918 my $rota = $builder->build({ source => 'Stockrotationrota' });
919 my $srrota = Koha::StockRotationRotas->find($rota->{rota_id});
921 $builder->build({
922 source => 'Stockrotationstage',
923 value => { rota_id => $rota->{rota_id} },
926 my $sritem = Koha::Items->find($item->{itemnumber});
927 $sritem->add_to_rota($rota->{rota_id});
930 Koha::StockRotationItems->find($item->{itemnumber})->stage_id,
931 $srrota->stockrotationstages->next->stage_id,
932 "Adding to a rota a new sritem item being assigned to its first stage."
935 my $newrota = $builder->build({ source => 'Stockrotationrota' });
937 my $srnewrota = Koha::StockRotationRotas->find($newrota->{rota_id});
939 $builder->build({
940 source => 'Stockrotationstage',
941 value => { rota_id => $newrota->{rota_id} },
944 $sritem->add_to_rota($newrota->{rota_id});
947 Koha::StockRotationItems->find($item->{itemnumber})->stage_id,
948 $srnewrota->stockrotationstages->next->stage_id,
949 "Moving an item results in that sritem being assigned to the new first stage."
952 $schema->storage->txn_rollback;
955 subtest 'Split subfields in Item2Marc (Bug 21774)' => sub {
956 plan tests => 3;
957 $schema->storage->txn_begin;
959 my $builder = t::lib::TestBuilder->new;
960 my $biblio = $builder->build({ source => 'Biblio', value => { frameworkcode => q{} } });
961 my $item = $builder->build({ source => 'Item', value => { biblionumber => $biblio->{biblionumber}, ccode => 'A|B' } });
963 Koha::MarcSubfieldStructures->search({ tagfield => '952', tagsubfield => '8' })->delete; # theoretical precaution
964 Koha::MarcSubfieldStructures->search({ kohafield => 'items.ccode' })->delete;
965 my $mapping = Koha::MarcSubfieldStructure->new(
967 frameworkcode => q{},
968 tagfield => '952',
969 tagsubfield => '8',
970 kohafield => 'items.ccode',
971 repeatable => 1
973 )->store;
974 Koha::Caches->get_instance->clear_from_cache( "MarcSubfieldStructure-" );
976 # Start testing
977 my $marc = C4::Items::Item2Marc( $item, $biblio->{biblionumber} );
978 my @subs = $marc->subfield( $mapping->tagfield, $mapping->tagsubfield );
979 is( @subs, 2, 'Expect two subfields' );
980 is( $subs[0], 'A', 'First subfield matches' );
981 is( $subs[1], 'B', 'Second subfield matches' );
983 $schema->storage->txn_rollback;
986 subtest 'ModItemFromMarc' => sub {
987 plan tests => 4;
988 $schema->storage->txn_begin;
990 my $builder = t::lib::TestBuilder->new;
991 my ($itemfield) = GetMarcFromKohaField( 'items.itemnumber' );
992 my $itemtype = $builder->build_object({ class => 'Koha::ItemTypes' });
993 my $biblio = $builder->build_sample_biblio;
994 my ( $lost_tag, $lost_sf ) = GetMarcFromKohaField( 'items.itemlost' );
995 my $item_record = new MARC::Record;
996 $item_record->append_fields(
997 MARC::Field->new(
998 $itemfield, '', '',
999 'y' => $itemtype->itemtype,
1001 MARC::Field->new(
1002 $itemfield, '', '',
1003 $lost_sf => '1',
1006 my (undef, undef, $itemnumber) = AddItemFromMarc($item_record,
1007 $biblio->biblionumber);
1009 my $item = Koha::Items->find($itemnumber);
1010 is( $item->itemlost, 1, 'itemlost picked from the item marc');
1012 $item->paidfor("this is something")->store;
1014 my $updated_item_record = new MARC::Record;
1015 $updated_item_record->append_fields(
1016 MARC::Field->new(
1017 $itemfield, '', '',
1018 'y' => $itemtype->itemtype,
1022 my $updated_item = ModItemFromMarc($updated_item_record, $biblio->biblionumber, $itemnumber);
1023 is( $updated_item->{itemlost}, 0, 'itemlost should have been reset to the default value in DB' );
1024 is( $updated_item->{paidfor}, "this is something", "Non mapped field has not been reset" );
1025 is( Koha::Items->find($itemnumber)->paidfor, "this is something" );
1027 $schema->storage->txn_rollback;