Bug 13853: (follow-up) unit 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
21 use MARC::Record;
22 use C4::Biblio;
23 use C4::Branch;
24 use Koha::Database;
25
26 use Test::More tests => 8;
27
28 BEGIN {
29     use_ok('C4::Items');
30     use_ok('Koha::Items');
31 }
32
33 my $dbh = C4::Context->dbh;
34 my $branches = GetBranches;
35 my ($branch1, $branch2) = keys %$branches;
36
37 subtest 'General Add, Get and Del tests' => sub {
38
39     plan tests => 6;
40
41     # Start transaction
42     $dbh->{AutoCommit} = 0;
43     $dbh->{RaiseError} = 1;
44
45     # Create a biblio instance for testing
46     C4::Context->set_preference('marcflavour', 'MARC21');
47     my ($bibnum, $bibitemnum) = get_biblio();
48
49     # Add an item.
50     my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $branch1, holdingbranch => $branch1 } , $bibnum);
51     cmp_ok($item_bibnum, '==', $bibnum, "New item is linked to correct biblionumber.");
52     cmp_ok($item_bibitemnum, '==', $bibitemnum, "New item is linked to correct biblioitemnumber.");
53
54     # Get item.
55     my $getitem = GetItem($itemnumber);
56     cmp_ok($getitem->{'itemnumber'}, '==', $itemnumber, "Retrieved item has correct itemnumber.");
57     cmp_ok($getitem->{'biblioitemnumber'}, '==', $item_bibitemnum, "Retrieved item has correct biblioitemnumber.");
58
59     # Modify item; setting barcode.
60     ModItem({ barcode => '987654321' }, $bibnum, $itemnumber);
61     my $moditem = GetItem($itemnumber);
62     cmp_ok($moditem->{'barcode'}, '==', '987654321', 'Modified item barcode successfully to: '.$moditem->{'barcode'} . '.');
63
64     # Delete item.
65     DelItem({ biblionumber => $bibnum, itemnumber => $itemnumber });
66     my $getdeleted = GetItem($itemnumber);
67     is($getdeleted->{'itemnumber'}, undef, "Item deleted as expected.");
68
69     $dbh->rollback;
70 };
71
72 subtest 'GetHiddenItemnumbers tests' => sub {
73
74     plan tests => 9;
75
76     # This sub is controlled by the OpacHiddenItems system preference.
77
78     # Start transaction
79     $dbh->{AutoCommit} = 0;
80     $dbh->{RaiseError} = 1;
81
82     # Create a new biblio
83     C4::Context->set_preference('marcflavour', 'MARC21');
84     my ($biblionumber, $biblioitemnumber) = get_biblio();
85
86     # Add branches if they don't exist
87     if (not defined GetBranchDetail('CPL')) {
88         ModBranch({add => 1, branchcode => 'CPL', branchname => 'Centerville'});
89     }
90     if (not defined GetBranchDetail('MPL')) {
91         ModBranch({add => 1, branchcode => 'MPL', branchname => 'Midway'});
92     }
93
94     # Add two items
95     my ($item1_bibnum, $item1_bibitemnum, $item1_itemnumber) = AddItem(
96             { homebranch => $branch1,
97               holdingbranch => $branch1,
98               withdrawn => 1 },
99             $biblionumber
100     );
101     my ($item2_bibnum, $item2_bibitemnum, $item2_itemnumber) = AddItem(
102             { homebranch => $branch2,
103               holdingbranch => $branch2,
104               withdrawn => 0 },
105             $biblionumber
106     );
107
108     my $opachiddenitems;
109     my @itemnumbers = ($item1_itemnumber,$item2_itemnumber);
110     my @hidden;
111     my @items;
112     push @items, GetItem( $item1_itemnumber );
113     push @items, GetItem( $item2_itemnumber );
114
115     # Empty OpacHiddenItems
116     C4::Context->set_preference('OpacHiddenItems','');
117     ok( !defined( GetHiddenItemnumbers( @items ) ),
118         "Hidden items list undef if OpacHiddenItems empty");
119
120     # Blank spaces
121     C4::Context->set_preference('OpacHiddenItems','  ');
122     ok( scalar GetHiddenItemnumbers( @items ) == 0,
123         "Hidden items list empty if OpacHiddenItems only contains blanks");
124
125     # One variable / value
126     $opachiddenitems = "
127         withdrawn: [1]";
128     C4::Context->set_preference( 'OpacHiddenItems', $opachiddenitems );
129     @hidden = GetHiddenItemnumbers( @items );
130     ok( scalar @hidden == 1, "Only one hidden item");
131     is( $hidden[0], $item1_itemnumber, "withdrawn=1 is hidden");
132
133     # One variable, two values
134     $opachiddenitems = "
135         withdrawn: [1,0]";
136     C4::Context->set_preference( 'OpacHiddenItems', $opachiddenitems );
137     @hidden = GetHiddenItemnumbers( @items );
138     ok( scalar @hidden == 2, "Two items hidden");
139     is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and withdrawn=0 hidden");
140
141     # Two variables, a value each
142     $opachiddenitems = "
143         withdrawn: [1]
144         homebranch: [$branch2]
145     ";
146     C4::Context->set_preference( 'OpacHiddenItems', $opachiddenitems );
147     @hidden = GetHiddenItemnumbers( @items );
148     ok( scalar @hidden == 2, "Two items hidden");
149     is_deeply( \@hidden, \@itemnumbers, "withdrawn=1 and homebranch=MPL hidden");
150
151     # Valid OpacHiddenItems, empty list
152     @items = ();
153     @hidden = GetHiddenItemnumbers( @items );
154     ok( scalar @hidden == 0, "Empty items list, no item hidden");
155
156     $dbh->rollback;
157 };
158
159 subtest 'GetItemsInfo tests' => sub {
160
161     plan tests => 4;
162
163     # Start transaction
164     $dbh->{AutoCommit} = 0;
165     $dbh->{RaiseError} = 1;
166
167     # Add a biblio
168     my ($biblionumber, $biblioitemnumber) = get_biblio();
169     # Add an item
170     my ($item_bibnum, $item_bibitemnum, $itemnumber)
171         = AddItem({
172                 homebranch    => $branch1,
173                 holdingbranch => $branch2
174             }, $biblionumber );
175
176     my $branch = GetBranchDetail( $branch1 );
177     $branch->{ opac_info } = "homebranch OPAC info";
178     ModBranch($branch);
179
180     $branch = GetBranchDetail( $branch2 );
181     $branch->{ opac_info } = "holdingbranch OPAC info";
182     ModBranch($branch);
183
184     my @results = GetItemsInfo( $biblionumber );
185     ok( @results, 'GetItemsInfo returns results');
186     is( $results[0]->{ home_branch_opac_info }, "homebranch OPAC info",
187         'GetItemsInfo returns the correct home branch OPAC info notice' );
188     is( $results[0]->{ holding_branch_opac_info }, "holdingbranch OPAC info",
189         'GetItemsInfo returns the correct holding branch OPAC info notice' );
190     is( exists( $results[0]->{ onsite_checkout } ), 1,
191         'GetItemsInfo returns a onsite_checkout key' );
192
193     $dbh->rollback;
194 };
195
196 subtest q{Test Koha::Database->schema()->resultset('Item')->itemtype()} => sub {
197
198     plan tests => 2;
199
200     # Start transaction
201     $dbh->{AutoCommit} = 0;
202     $dbh->{RaiseError} = 1;
203
204     my $schema = Koha::Database->new()->schema();
205
206     my $biblio =
207     $schema->resultset('Biblio')->create(
208         {
209             title       => "Test title",
210             biblioitems => [
211                 {
212                     itemtype => 'BIB_LEVEL',
213                     items    => [ { itype => "ITEM_LEVEL" } ]
214                 }
215             ]
216         }
217     );
218
219     my @bi = $biblio->biblioitems();
220     my ( $item ) = $bi[0]->items();
221
222     C4::Context->set_preference( 'item-level_itypes', 0 );
223     ok( $item->effective_itemtype() eq 'BIB_LEVEL', '$item->itemtype() returns biblioitem.itemtype when item-level_itypes is disabled' );
224
225     C4::Context->set_preference( 'item-level_itypes', 1 );
226     ok( $item->effective_itemtype() eq 'ITEM_LEVEL', '$item->itemtype() returns items.itype when item-level_itypes is enabled' );
227
228     $dbh->rollback;
229 };
230
231 subtest 'SearchItems test' => sub {
232     plan tests => 14;
233
234     # Start transaction
235     $dbh->{AutoCommit} = 0;
236     $dbh->{RaiseError} = 1;
237
238     C4::Context->set_preference('marcflavour', 'MARC21');
239     my ($biblionumber) = get_biblio();
240
241     # Add branches if they don't exist
242     if (not defined GetBranchDetail('CPL')) {
243         ModBranch({add => 1, branchcode => 'CPL', branchname => 'Centerville'});
244     }
245     if (not defined GetBranchDetail('MPL')) {
246         ModBranch({add => 1, branchcode => 'MPL', branchname => 'Midway'});
247     }
248
249     my (undef, $initial_items_count) = SearchItems(undef, {rows => 1});
250
251     # Add two items
252     my (undef, undef, $item1_itemnumber) = AddItem({
253         homebranch => 'CPL',
254         holdingbranch => 'CPL',
255     }, $biblionumber);
256     my (undef, undef, $item2_itemnumber) = AddItem({
257         homebranch => 'MPL',
258         holdingbranch => 'MPL',
259     }, $biblionumber);
260
261     my ($items, $total_results);
262
263     ($items, $total_results) = SearchItems();
264     is($total_results, $initial_items_count + 2, "Created 2 new items");
265     is(scalar @$items, $total_results, "SearchItems() returns all items");
266
267     ($items, $total_results) = SearchItems(undef, {rows => 1});
268     is($total_results, $initial_items_count + 2);
269     is(scalar @$items, 1, "SearchItems(undef, {rows => 1}) returns only 1 item");
270
271     # Search all items where homebranch = 'CPL'
272     my $filter = {
273         field => 'homebranch',
274         query => 'CPL',
275         operator => '=',
276     };
277     ($items, $total_results) = SearchItems($filter);
278     ok($total_results > 0, "There is at least one CPL item");
279     my $all_items_are_CPL = 1;
280     foreach my $item (@$items) {
281         if ($item->{homebranch} ne 'CPL') {
282             $all_items_are_CPL = 0;
283             last;
284         }
285     }
286     ok($all_items_are_CPL, "All items returned by SearchItems are from CPL");
287
288     # Search all items where homebranch != 'CPL'
289     $filter = {
290         field => 'homebranch',
291         query => 'CPL',
292         operator => '!=',
293     };
294     ($items, $total_results) = SearchItems($filter);
295     ok($total_results > 0, "There is at least one non-CPL item");
296     my $all_items_are_not_CPL = 1;
297     foreach my $item (@$items) {
298         if ($item->{homebranch} eq 'CPL') {
299             $all_items_are_not_CPL = 0;
300             last;
301         }
302     }
303     ok($all_items_are_not_CPL, "All items returned by SearchItems are not from CPL");
304
305     # Search all items where biblio title (245$a) is like 'Silence in the %'
306     $filter = {
307         field => 'marc:245$a',
308         query => 'Silence in the %',
309         operator => 'like',
310     };
311     ($items, $total_results) = SearchItems($filter);
312     ok($total_results >= 2, "There is at least 2 items with a biblio title like 'Silence in the %'");
313
314     # Search all items where biblio title is 'Silence in the library'
315     # and homebranch is 'CPL'
316     $filter = {
317         conjunction => 'AND',
318         filters => [
319             {
320                 field => 'marc:245$a',
321                 query => 'Silence in the %',
322                 operator => 'like',
323             },
324             {
325                 field => 'homebranch',
326                 query => 'CPL',
327                 operator => '=',
328             },
329         ],
330     };
331     ($items, $total_results) = SearchItems($filter);
332     my $found = 0;
333     foreach my $item (@$items) {
334         if ($item->{itemnumber} == $item1_itemnumber) {
335             $found = 1;
336             last;
337         }
338     }
339     ok($found, "item1 found");
340
341     my ($itemfield) = GetMarcFromKohaField('items.itemnumber', '');
342
343     # Create item subfield 'z' without link
344     $dbh->do('DELETE FROM marc_subfield_structure WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=""', undef, $itemfield);
345     $dbh->do('INSERT INTO marc_subfield_structure (tagfield, tagsubfield, frameworkcode) VALUES (?, "z", "")', undef, $itemfield);
346
347     # Clear cache
348     $C4::Context::context->{marcfromkohafield} = undef;
349     $C4::Biblio::inverted_field_map = undef;
350
351     my $item3_record = new MARC::Record;
352     $item3_record->append_fields(
353         new MARC::Field($itemfield, '', '', 'z' => 'foobar')
354     );
355     my (undef, undef, $item3_itemnumber) = AddItemFromMarc($item3_record,
356         $biblionumber);
357
358     # Search item where item subfield z is "foobar"
359     $filter = {
360         field => 'marc:' . $itemfield . '$z',
361         query => 'foobar',
362         operator => 'like',
363     };
364     ($items, $total_results) = SearchItems($filter);
365     ok(scalar @$items == 1, 'found 1 item with $z = "foobar"');
366
367     # Link $z to items.itemnotes (and make sure there is no other subfields
368     # linked to it)
369     $dbh->do('DELETE FROM marc_subfield_structure WHERE kohafield="items.itemnotes" AND frameworkcode=""', undef, $itemfield);
370     $dbh->do('UPDATE marc_subfield_structure SET kohafield="items.itemnotes" WHERE tagfield=? AND tagsubfield="z" AND frameworkcode=""', undef, $itemfield);
371
372     # Clear cache
373     $C4::Context::context->{marcfromkohafield} = undef;
374     $C4::Biblio::inverted_field_map = undef;
375
376     ModItemFromMarc($item3_record, $biblionumber, $item3_itemnumber);
377
378     # Make sure the link is used
379     my $item3 = GetItem($item3_itemnumber);
380     ok($item3->{itemnotes} eq 'foobar', 'itemnotes eq "foobar"');
381
382     # Do the same search again.
383     # This time it will search in items.itemnotes
384     ($items, $total_results) = SearchItems($filter);
385     ok(scalar @$items == 1, 'found 1 item with itemnotes = "foobar"');
386
387     my $cpl_items = SearchItemsByField( 'homebranch', 'CPL');
388     is( ( $cpl_items and scalar( @$cpl_items ) ), 1, 'SearchItemsByField should return something' );
389
390     $dbh->rollback;
391 };
392
393 subtest 'Koha::Item(s) tests' => sub {
394
395     plan tests => 5;
396
397     # Start transaction
398     my $schema = Koha::Database->new()->schema();
399     $schema->storage->txn_begin();
400     $dbh->{RaiseError} = 1;
401
402     # Create a biblio and item for testing
403     C4::Context->set_preference('marcflavour', 'MARC21');
404     my ($bibnum, $bibitemnum) = get_biblio();
405     my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $branch1, holdingbranch => $branch2 } , $bibnum);
406
407     # Get item.
408     my $item = Koha::Items->find( $itemnumber );
409     is( ref($item), 'Koha::Item', "Got Koha::Item" );
410
411     my $homebranch = $item->home_branch();
412     is( ref($homebranch), 'Koha::Branch', "Got Koha::Branch from home_branch method" );
413     is( $homebranch->branchcode(), $branch1, "Home branch code matches homebranch" );
414
415     my $holdingbranch = $item->holding_branch();
416     is( ref($holdingbranch), 'Koha::Branch', "Got Koha::Branch from holding_branch method" );
417     is( $holdingbranch->branchcode(), $branch2, "Home branch code matches holdingbranch" );
418 };
419
420 # Helper method to set up a Biblio.
421 sub get_biblio {
422     my $bib = MARC::Record->new();
423     $bib->append_fields(
424         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
425         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
426     );
427     my ($bibnum, $bibitemnum) = AddBiblio($bib, '');
428     return ($bibnum, $bibitemnum);
429 }