Bug 20045: Fix Selenium tests
[koha.git] / t / db_dependent / Items.t
1 #!/usr/bin/perl
2 #
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 2 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 #
18
19 use Modern::Perl;
20 use Data::Dumper;
21
22 use MARC::Record;
23 use C4::Biblio;
24 use Koha::Database;
25 use Koha::DateUtils qw( dt_from_string );
26 use Koha::Library;
27 use Koha::DateUtils;
28
29 use t::lib::Mocks;
30 use t::lib::TestBuilder;
31
32 use Koha::MarcSubfieldStructures;
33 use Koha::Caches;
34
35 use Test::More tests => 13;
36
37 use Test::Warn;
38
39 BEGIN {
40     use_ok('C4::Items');
41     use_ok('Koha::Items');
42 }
43
44 my $schema = Koha::Database->new->schema;
45 my $location = 'My Location';
46
47 subtest 'General Add, Get and Del tests' => sub {
48
49     plan tests => 16;
50
51     $schema->storage->txn_begin;
52
53     my $builder = t::lib::TestBuilder->new;
54     my $library = $builder->build({
55         source => 'Branch',
56     });
57     my $itemtype = $builder->build({
58         source => 'Itemtype',
59     });
60
61     # Create a biblio instance for testing
62     t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
63     my ($bibnum, $bibitemnum) = get_biblio();
64
65     # Add an item.
66     my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $library->{branchcode}, holdingbranch => $library->{branchcode}, location => $location, itype => $itemtype->{itemtype} } , $bibnum);
67     cmp_ok($item_bibnum, '==', $bibnum, "New item is linked to correct biblionumber.");
68     cmp_ok($item_bibitemnum, '==', $bibitemnum, "New item is linked to correct biblioitemnumber.");
69
70     # Get item.
71     my $getitem = GetItem($itemnumber);
72     cmp_ok($getitem->{'itemnumber'}, '==', $itemnumber, "Retrieved item has correct itemnumber.");
73     cmp_ok($getitem->{'biblioitemnumber'}, '==', $item_bibitemnum, "Retrieved item has correct biblioitemnumber.");
74     is( $getitem->{location}, $location, "The location should not have been modified" );
75     is( $getitem->{permanent_location}, $location, "The permanent_location should have been set to the location value" );
76
77     # Modify item; setting barcode.
78     ModItem({ barcode => '987654321' }, $bibnum, $itemnumber);
79     my $moditem = GetItem($itemnumber);
80     cmp_ok($moditem->{'barcode'}, '==', '987654321', 'Modified item barcode successfully to: '.$moditem->{'barcode'} . '.');
81
82     # Delete item.
83     DelItem({ biblionumber => $bibnum, itemnumber => $itemnumber });
84     my $getdeleted = GetItem($itemnumber);
85     is($getdeleted->{'itemnumber'}, undef, "Item deleted as expected.");
86
87     ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $library->{branchcode}, holdingbranch => $library->{branchcode}, location => $location, permanent_location => 'my permanent location', itype => $itemtype->{itemtype} } , $bibnum);
88     $getitem = GetItem($itemnumber);
89     is( $getitem->{location}, $location, "The location should not have been modified" );
90     is( $getitem->{permanent_location}, 'my permanent location', "The permanent_location should not have modified" );
91
92     ModItem({ location => $location }, $bibnum, $itemnumber);
93     $getitem = GetItem($itemnumber);
94     is( $getitem->{location}, $location, "The location should have been set to correct location" );
95     is( $getitem->{permanent_location}, $location, "The permanent_location should have been set to location" );
96
97     ModItem({ location => 'CART' }, $bibnum, $itemnumber);
98     $getitem = GetItem($itemnumber);
99     is( $getitem->{location}, 'CART', "The location should have been set to CART" );
100     is( $getitem->{permanent_location}, $location, "The permanent_location should not have been set to CART" );
101
102     t::lib::Mocks::mock_preference('item-level_itypes', '1');
103     $getitem = GetItem($itemnumber);
104     is( $getitem->{itype}, $itemtype->{itemtype}, "Itemtype set correctly when using item-level_itypes" );
105     t::lib::Mocks::mock_preference('item-level_itypes', '0');
106     $getitem = GetItem($itemnumber);
107     is( $getitem->{itype}, undef, "Itemtype set correctly when not using item-level_itypes" );
108
109     $schema->storage->txn_rollback;
110 };
111
112 subtest 'ModItem tests' => sub {
113     plan tests => 6;
114
115     $schema->storage->txn_begin;
116
117     my $builder = t::lib::TestBuilder->new;
118     my $item = $builder->build({
119         source => 'Item',
120         value  => {
121             itemlost     => 0,
122             damaged      => 0,
123             withdrawn    => 0,
124             itemlost_on  => undef,
125             damaged_on   => undef,
126             withdrawn_on => undef,
127         }
128     });
129
130     my @fields = qw( itemlost withdrawn damaged );
131     for my $field (@fields) {
132         $item->{$field} = 1;
133         ModItem( $item, $item->{biblionumber}, $item->{itemnumber} );
134         my $post_mod_item = Koha::Items->find({ itemnumber => $item->{itemnumber} })->unblessed;
135         is( output_pref({ str => $post_mod_item->{$field."_on"}, dateonly => 1 }), output_pref({ dt => dt_from_string(), dateonly => 1 }), "When updating $field, $field"."_on is updated" );
136
137         $item->{$field} = 0;
138         ModItem( $item, $item->{biblionumber}, $item->{itemnumber} );
139         $post_mod_item = Koha::Items->find({ itemnumber => $item->{itemnumber} })->unblessed;
140         is( $post_mod_item->{$field."_on"}, undef, "When clearing $field, $field"."_on is cleared" );
141     }
142
143     $schema->storage->txn_rollback;
144
145 };
146
147 subtest 'GetHiddenItemnumbers tests' => sub {
148
149     plan tests => 9;
150
151     # This sub is controlled by the OpacHiddenItems system preference.
152
153     $schema->storage->txn_begin;
154
155     my $builder = t::lib::TestBuilder->new;
156     my $library1 = $builder->build({
157         source => 'Branch',
158     });
159
160     my $library2 = $builder->build({
161         source => 'Branch',
162     });
163     my $itemtype = $builder->build({
164         source => 'Itemtype',
165     });
166
167     # Create a new biblio
168     t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
169     my ($biblionumber, $biblioitemnumber) = get_biblio();
170
171     # Add two items
172     my ( $item1_bibnum, $item1_bibitemnum, $item1_itemnumber ) = AddItem(
173         {
174             homebranch    => $library1->{branchcode},
175             holdingbranch => $library1->{branchcode},
176             withdrawn     => 1,
177             itype         => $itemtype->{itemtype},
178         },
179         $biblionumber
180     );
181     my ( $item2_bibnum, $item2_bibitemnum, $item2_itemnumber ) = AddItem(
182         {
183             homebranch    => $library2->{branchcode},
184             holdingbranch => $library2->{branchcode},
185             withdrawn     => 0,
186             itype         => $itemtype->{itemtype},
187         },
188         $biblionumber
189     );
190
191     my $opachiddenitems;
192     my @itemnumbers = ($item1_itemnumber,$item2_itemnumber);
193     my @hidden;
194     my @items;
195     push @items, GetItem( $item1_itemnumber );
196     push @items, GetItem( $item2_itemnumber );
197
198     # Empty OpacHiddenItems
199     t::lib::Mocks::mock_preference('OpacHiddenItems','');
200     ok( !defined( GetHiddenItemnumbers( @items ) ),
201         "Hidden items list undef if OpacHiddenItems empty");
202
203     # Blank spaces
204     t::lib::Mocks::mock_preference('OpacHiddenItems','  ');
205     ok( scalar GetHiddenItemnumbers( @items ) == 0,
206         "Hidden items list empty if OpacHiddenItems only contains blanks");
207
208     # One variable / value
209     $opachiddenitems = "
210         withdrawn: [1]";
211     t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
212     @hidden = GetHiddenItemnumbers( @items );
213     ok( scalar @hidden == 1, "Only one hidden item");
214     is( $hidden[0], $item1_itemnumber, "withdrawn=1 is hidden");
215
216     # One variable, two values
217     $opachiddenitems = "
218         withdrawn: [1,0]";
219     t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
220     @hidden = GetHiddenItemnumbers( @items );
221     ok( scalar @hidden == 2, "Two items hidden");
222     is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and withdrawn=0 hidden");
223
224     # Two variables, a value each
225     $opachiddenitems = "
226         withdrawn: [1]
227         homebranch: [$library2->{branchcode}]
228     ";
229     t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
230     @hidden = GetHiddenItemnumbers( @items );
231     ok( scalar @hidden == 2, "Two items hidden");
232     is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and homebranch library2 hidden");
233
234     # Valid OpacHiddenItems, empty list
235     @items = ();
236     @hidden = GetHiddenItemnumbers( @items );
237     ok( scalar @hidden == 0, "Empty items list, no item hidden");
238
239     $schema->storage->txn_rollback;
240 };
241
242 subtest 'GetItemsInfo tests' => sub {
243
244     plan tests => 4;
245
246     $schema->storage->txn_begin;
247
248     my $builder = t::lib::TestBuilder->new;
249     my $library1 = $builder->build({
250         source => 'Branch',
251     });
252     my $library2 = $builder->build({
253         source => 'Branch',
254     });
255     my $itemtype = $builder->build({
256         source => 'Itemtype',
257     });
258
259     # Add a biblio
260     my ($biblionumber, $biblioitemnumber) = get_biblio();
261     # Add an item
262     my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
263         {
264             homebranch    => $library1->{branchcode},
265             holdingbranch => $library2->{branchcode},
266             itype         => $itemtype->{itemtype},
267         },
268         $biblionumber
269     );
270
271     my $library = Koha::Libraries->find( $library1->{branchcode} );
272     $library->opac_info("homebranch OPAC info");
273     $library->store;
274
275     $library = Koha::Libraries->find( $library2->{branchcode} );
276     $library->opac_info("holdingbranch OPAC info");
277     $library->store;
278
279     my @results = GetItemsInfo( $biblionumber );
280     ok( @results, 'GetItemsInfo returns results');
281     is( $results[0]->{ home_branch_opac_info }, "homebranch OPAC info",
282         'GetItemsInfo returns the correct home branch OPAC info notice' );
283     is( $results[0]->{ holding_branch_opac_info }, "holdingbranch OPAC info",
284         'GetItemsInfo returns the correct holding branch OPAC info notice' );
285     is( exists( $results[0]->{ onsite_checkout } ), 1,
286         'GetItemsInfo returns a onsite_checkout key' );
287
288     $schema->storage->txn_rollback;
289 };
290
291 subtest q{Test Koha::Database->schema()->resultset('Item')->itemtype()} => sub {
292
293     plan tests => 4;
294
295     $schema->storage->txn_begin;
296
297     my $biblio = $schema->resultset('Biblio')->create({
298         title       => "Test title",
299         datecreated => dt_from_string,
300         biblioitems => [ { itemtype => 'BIB_LEVEL' } ],
301     });
302     my $biblioitem = $biblio->biblioitems->first;
303     my $item = $schema->resultset('Item')->create({
304         biblioitemnumber => $biblioitem->biblioitemnumber,
305         biblionumber     => $biblio->biblionumber,
306         itype            => "ITEM_LEVEL",
307     });
308
309     t::lib::Mocks::mock_preference( 'item-level_itypes', 0 );
310     is( $item->effective_itemtype(), 'BIB_LEVEL', '$item->itemtype() returns biblioitem.itemtype when item-level_itypes is disabled' );
311
312     t::lib::Mocks::mock_preference( 'item-level_itypes', 1 );
313     is( $item->effective_itemtype(), 'ITEM_LEVEL', '$item->itemtype() returns items.itype when item-level_itypes is enabled' );
314
315     # If itemtype is not defined and item-level_level item types are set
316     # fallback to biblio-level itemtype (Bug 14651) and warn
317     $item->itype( undef );
318     $item->update();
319     my $effective_itemtype;
320     warning_is { $effective_itemtype = $item->effective_itemtype() }
321                 "item-level_itypes set but no itemtype set for item (".$item->itemnumber.")",
322                 '->effective_itemtype() raises a warning when falling back to bib-level';
323
324     ok( defined $effective_itemtype &&
325                 $effective_itemtype eq 'BIB_LEVEL',
326         '$item->effective_itemtype() falls back to biblioitems.itemtype when item-level_itypes is enabled but undef' );
327
328     $schema->storage->txn_rollback;
329 };
330
331 subtest 'SearchItems test' => sub {
332     plan tests => 14;
333
334     $schema->storage->txn_begin;
335     my $dbh = C4::Context->dbh;
336     my $builder = t::lib::TestBuilder->new;
337
338     my $library1 = $builder->build({
339         source => 'Branch',
340     });
341     my $library2 = $builder->build({
342         source => 'Branch',
343     });
344     my $itemtype = $builder->build({
345         source => 'Itemtype',
346     });
347
348     t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
349     my $cpl_items_before = SearchItemsByField( 'homebranch', $library1->{branchcode});
350
351     my ($biblionumber) = get_biblio();
352
353     my (undef, $initial_items_count) = SearchItems(undef, {rows => 1});
354
355     # Add two items
356     my (undef, undef, $item1_itemnumber) = AddItem({
357         homebranch => $library1->{branchcode},
358         holdingbranch => $library1->{branchcode},
359         itype => $itemtype->{itemtype},
360     }, $biblionumber);
361     my (undef, undef, $item2_itemnumber) = AddItem({
362         homebranch => $library2->{branchcode},
363         holdingbranch => $library2->{branchcode},
364         itype => $itemtype->{itemtype},
365     }, $biblionumber);
366
367     my ($items, $total_results);
368
369     ($items, $total_results) = SearchItems();
370     is($total_results, $initial_items_count + 2, "Created 2 new items");
371     is(scalar @$items, $total_results, "SearchItems() returns all items");
372
373     ($items, $total_results) = SearchItems(undef, {rows => 1});
374     is($total_results, $initial_items_count + 2);
375     is(scalar @$items, 1, "SearchItems(undef, {rows => 1}) returns only 1 item");
376
377     # Search all items where homebranch = 'CPL'
378     my $filter = {
379         field => 'homebranch',
380         query => $library1->{branchcode},
381         operator => '=',
382     };
383     ($items, $total_results) = SearchItems($filter);
384     ok($total_results > 0, "There is at least one CPL item");
385     my $all_items_are_CPL = 1;
386     foreach my $item (@$items) {
387         if ($item->{homebranch} ne $library1->{branchcode}) {
388             $all_items_are_CPL = 0;
389             last;
390         }
391     }
392     ok($all_items_are_CPL, "All items returned by SearchItems are from CPL");
393
394     # Search all items where homebranch != 'CPL'
395     $filter = {
396         field => 'homebranch',
397         query => $library1->{branchcode},
398         operator => '!=',
399     };
400     ($items, $total_results) = SearchItems($filter);
401     ok($total_results > 0, "There is at least one non-CPL item");
402     my $all_items_are_not_CPL = 1;
403     foreach my $item (@$items) {
404         if ($item->{homebranch} eq $library1->{branchcode}) {
405             $all_items_are_not_CPL = 0;
406             last;
407         }
408     }
409     ok($all_items_are_not_CPL, "All items returned by SearchItems are not from CPL");
410
411     # Search all items where biblio title (245$a) is like 'Silence in the %'
412     $filter = {
413         field => 'marc:245$a',
414         query => 'Silence in the %',
415         operator => 'like',
416     };
417     ($items, $total_results) = SearchItems($filter);
418     ok($total_results >= 2, "There is at least 2 items with a biblio title like 'Silence in the %'");
419
420     # Search all items where biblio title is 'Silence in the library'
421     # and homebranch is 'CPL'
422     $filter = {
423         conjunction => 'AND',
424         filters => [
425             {
426                 field => 'marc:245$a',
427                 query => 'Silence in the %',
428                 operator => 'like',
429             },
430             {
431                 field => 'homebranch',
432                 query => $library1->{branchcode},
433                 operator => '=',
434             },
435         ],
436     };
437     ($items, $total_results) = SearchItems($filter);
438     my $found = 0;
439     foreach my $item (@$items) {
440         if ($item->{itemnumber} == $item1_itemnumber) {
441             $found = 1;
442             last;
443         }
444     }
445     ok($found, "item1 found");
446
447     my $frameworkcode = q||;
448     my ($itemfield) = GetMarcFromKohaField('items.itemnumber', $frameworkcode);
449
450     # Create item subfield 'z' without link
451     $dbh->do('DELETE FROM marc_subfield_structure WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
452     $dbh->do('INSERT INTO marc_subfield_structure (tagfield, tagsubfield, frameworkcode) VALUES (?, "z", ?)', undef, $itemfield, $frameworkcode);
453
454     # Clear cache
455     my $cache = Koha::Caches->get_instance();
456     $cache->clear_from_cache("MarcStructure-0-$frameworkcode");
457     $cache->clear_from_cache("MarcStructure-1-$frameworkcode");
458     $cache->clear_from_cache("default_value_for_mod_marc-");
459     $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
460
461     my $item3_record = new MARC::Record;
462     $item3_record->append_fields(
463         new MARC::Field(
464             $itemfield, '', '',
465             'z' => 'foobar',
466             'y' => $itemtype->{itemtype}
467         )
468     );
469     my (undef, undef, $item3_itemnumber) = AddItemFromMarc($item3_record,
470         $biblionumber);
471
472     # Search item where item subfield z is "foobar"
473     $filter = {
474         field => 'marc:' . $itemfield . '$z',
475         query => 'foobar',
476         operator => 'like',
477     };
478     ($items, $total_results) = SearchItems($filter);
479     ok(scalar @$items == 1, 'found 1 item with $z = "foobar"');
480
481     # Link $z to items.itemnotes (and make sure there is no other subfields
482     # linked to it)
483     $dbh->do('DELETE FROM marc_subfield_structure WHERE kohafield="items.itemnotes" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
484     $dbh->do('UPDATE marc_subfield_structure SET kohafield="items.itemnotes" WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=?', undef, $itemfield, $frameworkcode);
485
486     # Clear cache
487     $cache->clear_from_cache("MarcStructure-0-$frameworkcode");
488     $cache->clear_from_cache("MarcStructure-1-$frameworkcode");
489     $cache->clear_from_cache("default_value_for_mod_marc-");
490     $cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
491
492     ModItemFromMarc($item3_record, $biblionumber, $item3_itemnumber);
493
494     # Make sure the link is used
495     my $item3 = GetItem($item3_itemnumber);
496     ok($item3->{itemnotes} eq 'foobar', 'itemnotes eq "foobar"');
497
498     # Do the same search again.
499     # This time it will search in items.itemnotes
500     ($items, $total_results) = SearchItems($filter);
501     ok(scalar @$items == 1, 'found 1 item with itemnotes = "foobar"');
502
503     my $cpl_items_after = SearchItemsByField( 'homebranch', $library1->{branchcode});
504     is( ( scalar( @$cpl_items_after ) - scalar ( @$cpl_items_before ) ), 1, 'SearchItemsByField should return something' );
505
506     $schema->storage->txn_rollback;
507 };
508
509 subtest 'Koha::Item(s) tests' => sub {
510
511     plan tests => 5;
512
513     $schema->storage->txn_begin();
514
515     my $builder = t::lib::TestBuilder->new;
516     my $library1 = $builder->build({
517         source => 'Branch',
518     });
519     my $library2 = $builder->build({
520         source => 'Branch',
521     });
522     my $itemtype = $builder->build({
523         source => 'Itemtype',
524     });
525
526     # Create a biblio and item for testing
527     t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
528     my ($bibnum, $bibitemnum) = get_biblio();
529     my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
530         {
531             homebranch    => $library1->{branchcode},
532             holdingbranch => $library2->{branchcode},
533             itype         => $itemtype->{itemtype},
534         },
535         $bibnum
536     );
537
538     # Get item.
539     my $item = Koha::Items->find( $itemnumber );
540     is( ref($item), 'Koha::Item', "Got Koha::Item" );
541
542     my $homebranch = $item->home_branch();
543     is( ref($homebranch), 'Koha::Library', "Got Koha::Library from home_branch method" );
544     is( $homebranch->branchcode(), $library1->{branchcode}, "Home branch code matches homebranch" );
545
546     my $holdingbranch = $item->holding_branch();
547     is( ref($holdingbranch), 'Koha::Library', "Got Koha::Library from holding_branch method" );
548     is( $holdingbranch->branchcode(), $library2->{branchcode}, "Home branch code matches holdingbranch" );
549
550     $schema->storage->txn_rollback;
551 };
552
553 subtest 'C4::Biblio::EmbedItemsInMarcBiblio' => sub {
554     plan tests => 7;
555
556     $schema->storage->txn_begin();
557
558     my $builder = t::lib::TestBuilder->new;
559     my $library1 = $builder->build({
560         source => 'Branch',
561     });
562     my $library2 = $builder->build({
563         source => 'Branch',
564     });
565     my $itemtype = $builder->build({
566         source => 'Itemtype',
567     });
568
569     my ( $biblionumber, $biblioitemnumber ) = get_biblio();
570     my $item_infos = [
571         { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
572         { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
573         { homebranch => $library1->{branchcode}, holdingbranch => $library1->{branchcode} },
574         { homebranch => $library2->{branchcode}, holdingbranch => $library2->{branchcode} },
575         { homebranch => $library2->{branchcode}, holdingbranch => $library2->{branchcode} },
576         { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
577         { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
578         { homebranch => $library1->{branchcode}, holdingbranch => $library2->{branchcode} },
579     ];
580     my $number_of_items = scalar @$item_infos;
581     my $number_of_items_with_homebranch_is_CPL =
582       grep { $_->{homebranch} eq $library1->{branchcode} } @$item_infos;
583
584     my @itemnumbers;
585     for my $item_info (@$item_infos) {
586         my ( undef, undef, $itemnumber ) = AddItem(
587             {
588                 homebranch    => $item_info->{homebranch},
589                 holdingbranch => $item_info->{holdingbanch},
590                 itype         => $itemtype->{itemtype},
591             },
592             $biblionumber
593         );
594         push @itemnumbers, $itemnumber;
595     }
596
597     # Emptied the OpacHiddenItems pref
598     t::lib::Mocks::mock_preference( 'OpacHiddenItems', '' );
599
600     my ($itemfield) =
601       C4::Biblio::GetMarcFromKohaField( 'items.itemnumber', '' );
602     my $record = C4::Biblio::GetMarcBiblio({ biblionumber => $biblionumber });
603     warning_is { C4::Biblio::EmbedItemsInMarcBiblio() }
604     { carped => 'EmbedItemsInMarcBiblio: No MARC record passed' },
605       'Should crap is no record passed.';
606
607     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber );
608     my @items = $record->field($itemfield);
609     is( scalar @items, $number_of_items, 'Should return all items' );
610
611     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber,
612         [ $itemnumbers[1], $itemnumbers[3] ] );
613     @items = $record->field($itemfield);
614     is( scalar @items, 2, 'Should return all items present in the list' );
615
616     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, undef, 1 );
617     @items = $record->field($itemfield);
618     is( scalar @items, $number_of_items, 'Should return all items for opac' );
619
620     my $opachiddenitems = "
621         homebranch: ['$library1->{branchcode}']";
622     t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
623
624     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber );
625     @items = $record->field($itemfield);
626     is( scalar @items,
627         $number_of_items,
628         'Even with OpacHiddenItems set, all items should have been embedded' );
629
630     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, undef, 1 );
631     @items = $record->field($itemfield);
632     is(
633         scalar @items,
634         $number_of_items - $number_of_items_with_homebranch_is_CPL,
635 'For OPAC, the pref OpacHiddenItems should have been take into account. Only items with homebranch ne CPL should have been embedded'
636     );
637
638     $opachiddenitems = "
639         homebranch: ['$library1->{branchcode}', '$library2->{branchcode}']";
640     t::lib::Mocks::mock_preference( 'OpacHiddenItems', $opachiddenitems );
641     C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, undef, 1 );
642     @items = $record->field($itemfield);
643     is(
644         scalar @items,
645         0,
646 'For OPAC, If all items are hidden, no item should have been embedded'
647     );
648
649     $schema->storage->txn_rollback;
650 };
651
652
653 subtest 'C4::Items::_build_default_values_for_mod_marc' => sub {
654     plan tests => 4;
655
656     $schema->storage->txn_begin();
657
658     my $builder = t::lib::TestBuilder->new;
659     my $framework = $builder->build({ source => 'BiblioFramework' });
660
661     # Link biblio.biblionumber and biblioitems.biblioitemnumber to avoid _koha_marc_update_bib_ids to fail with 'no biblio[item]number tag for framework"
662     Koha::MarcSubfieldStructures->search({ frameworkcode => '', tagfield => '999', tagsubfield => [ 'c', 'd' ] })->delete;
663     Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '999', tagsubfield => 'c', kohafield => 'biblio.biblionumber' })->store;
664     Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '999', tagsubfield => 'd', kohafield => 'biblioitems.biblioitemnumber' })->store;
665
666     # Same for item fields: itemnumber, barcode, itype
667     Koha::MarcSubfieldStructures->search({ frameworkcode => '', tagfield => '952', tagsubfield => [ '9', 'p', 'y' ] })->delete;
668     Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => '9', kohafield => 'items.itemnumber' })->store;
669     Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'p', kohafield => 'items.barcode' })->store;
670     Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'y', kohafield => 'items.itype' })->store;
671     Koha::Caches->get_instance->clear_from_cache( "MarcSubfieldStructure-" );
672
673     my $itemtype = $builder->build({ source => 'Itemtype' })->{itemtype};
674
675     # Create a record with a barcode
676     my ($biblionumber) = get_biblio( $framework->{frameworkcode} );
677     my $item_record = new MARC::Record;
678     my $a_barcode = 'a barcode';
679     my $barcode_field = MARC::Field->new(
680         '952', ' ', ' ',
681         p => $a_barcode,
682         y => $itemtype
683     );
684     my $itemtype_field = MARC::Field->new(
685         '952', ' ', ' ',
686         y => $itemtype
687     );
688     $item_record->append_fields( $barcode_field );
689     my (undef, undef, $item_itemnumber) = AddItemFromMarc($item_record, $biblionumber);
690
691     # Make sure everything has been set up
692     my $item = GetItem($item_itemnumber);
693     is( $item->{barcode}, $a_barcode, 'Everything has been set up correctly, the barcode is defined as expected' );
694
695     # Delete the barcode field and save the record
696     $item_record->delete_fields( $barcode_field );
697     $item_record->append_fields( $itemtype_field ); # itemtype is mandatory
698     ModItemFromMarc($item_record, $biblionumber, $item_itemnumber);
699     $item = GetItem($item_itemnumber);
700     is( $item->{barcode}, undef, 'The default value should have been set to the barcode, the field is mapped to a kohafield' );
701
702     # Re-add the barcode field and save the record
703     $item_record->append_fields( $barcode_field );
704     ModItemFromMarc($item_record, $biblionumber, $item_itemnumber);
705     $item = GetItem($item_itemnumber);
706     is( $item->{barcode}, $a_barcode, 'Everything has been set up correctly, the barcode is defined as expected' );
707
708     # Remove the mapping for barcode
709     Koha::MarcSubfieldStructures->search({ frameworkcode => '', tagfield => '952', tagsubfield => 'p' })->delete;
710
711     # And make sure the caches are cleared
712     my $cache = Koha::Caches->get_instance();
713     $cache->clear_from_cache("default_value_for_mod_marc-");
714     $cache->clear_from_cache("MarcSubfieldStructure-");
715
716     # Update the MARC field with another value
717     $item_record->delete_fields( $barcode_field );
718     my $another_barcode = 'another_barcode';
719     my $another_barcode_field = MARC::Field->new(
720         '952', ' ', ' ',
721         p => $another_barcode,
722     );
723     $item_record->append_fields( $another_barcode_field );
724     # The DB value should not have been updated
725     ModItemFromMarc($item_record, $biblionumber, $item_itemnumber);
726     $item = GetItem($item_itemnumber);
727     is ( $item->{barcode}, $a_barcode, 'items.barcode is not mapped anymore, so the DB column has not been updated' );
728
729     $cache->clear_from_cache("default_value_for_mod_marc-");
730     $cache->clear_from_cache( "MarcSubfieldStructure-" );
731     $schema->storage->txn_rollback;
732 };
733
734 subtest '_mod_item_dates' => sub {
735     plan tests => 11;
736
737     is( C4::Items::_mod_item_dates(), undef, 'Call without parameters' );
738     is( C4::Items::_mod_item_dates(1), undef, 'Call without hashref' );
739
740     my $orgitem;
741     my $item = {
742         itemcallnumber  => 'V II 149 1963',
743         barcode         => '109304',
744     };
745     $orgitem = { %$item };
746     C4::Items::_mod_item_dates($item);
747     is_deeply( $item, $orgitem, 'No dates passed to _mod_item_dates' );
748
749     # add two correct dates
750     t::lib::Mocks::mock_preference('dateformat', 'us');
751     $item->{dateaccessioned} = '01/31/2016';
752     $item->{onloan} =  $item->{dateaccessioned};
753     $orgitem = { %$item };
754     C4::Items::_mod_item_dates($item);
755     is( $item->{dateaccessioned}, '2016-01-31', 'dateaccessioned is fine' );
756     is( $item->{onloan}, '2016-01-31', 'onloan is fine too' );
757
758
759     # add some invalid dates
760     $item->{notexistingcolumndate} = '13/1/2015'; # wrong format
761     $item->{anotherdate} = 'tralala'; # even worse
762     $item->{myzerodate} = '0000-00-00'; # wrong too
763     C4::Items::_mod_item_dates($item);
764     is( $item->{notexistingcolumndate}, undef, 'Invalid date became NULL' );
765     is( $item->{anotherdate}, undef, 'Second invalid date became NULL too' );
766     is( $item->{myzerodate}, undef, '0000-00-00 became NULL too' );
767
768     # check if itemlost_on was not touched
769     $item->{itemlost_on} = '12345678';
770     $item->{withdrawn_on} = '12/31/2015 23:59:00';
771     $item->{damaged_on} = '01/20/2017 09:00:00';
772     $orgitem = { %$item };
773     C4::Items::_mod_item_dates($item);
774     is_deeply( $item, $orgitem, 'Colums with _on are not touched' );
775
776     t::lib::Mocks::mock_preference('dateformat', 'metric');
777     $item->{dateaccessioned} = '01/31/2016'; #wrong
778     $item->{yetanotherdatetime} = '20/01/2016 13:58:00'; #okay
779     C4::Items::_mod_item_dates($item);
780     is( $item->{dateaccessioned}, undef, 'dateaccessioned wrong format' );
781     is( $item->{yetanotherdatetime}, '2016-01-20 13:58:00',
782         'yetanotherdatetime is ok' );
783 };
784
785 subtest 'get_hostitemnumbers_of' => sub {
786     plan tests => 1;
787
788     my $bib = MARC::Record->new();
789     $bib->append_fields(
790         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
791         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
792         MARC::Field->new('773', ' ', ' ', b => 'b without 0 or 9'),
793     );
794     my ($biblionumber, $bibitemnum) = AddBiblio($bib, '');
795
796     my @itemnumbers = C4::Items::get_hostitemnumbers_of( $biblionumber );
797     is( @itemnumbers, 0, );
798 };
799
800 # Helper method to set up a Biblio.
801 sub get_biblio {
802     my ( $frameworkcode ) = @_;
803     $frameworkcode //= '';
804     my $bib = MARC::Record->new();
805     $bib->append_fields(
806         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
807         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
808     );
809     my ($bibnum, $bibitemnum) = AddBiblio($bib, $frameworkcode);
810     return ($bibnum, $bibitemnum);
811 }