Merge remote-tracking branch 'origin/new/bug_7178'
[koha.git] / C4 / Items.pm
1 package C4::Items;
2
3 # Copyright 2007 LibLime, Inc.
4 # Parts Copyright Biblibre 2010
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 use strict;
22 #use warnings; FIXME - Bug 2505
23
24 use Carp;
25 use C4::Context;
26 use C4::Koha;
27 use C4::Biblio;
28 use C4::Dates qw/format_date format_date_in_iso/;
29 use MARC::Record;
30 use C4::ClassSource;
31 use C4::Log;
32 use List::MoreUtils qw/any/;
33 use C4::Search;
34 use Data::Dumper; # used as part of logging item record changes, not just for
35                   # debugging; so please don't remove this
36
37 use vars qw($VERSION @ISA @EXPORT);
38
39 BEGIN {
40     $VERSION = 3.01;
41
42         require Exporter;
43     @ISA = qw( Exporter );
44
45     # function exports
46     @EXPORT = qw(
47         GetItem
48         AddItemFromMarc
49         AddItem
50         AddItemBatchFromMarc
51         ModItemFromMarc
52                 Item2Marc
53         ModItem
54         ModDateLastSeen
55         ModItemTransfer
56         DelItem
57     
58         CheckItemPreSave
59     
60         GetItemStatus
61         GetItemLocation
62         GetLostItems
63         GetItemsForInventory
64         GetItemsCount
65         GetItemInfosOf
66         GetItemsByBiblioitemnumber
67         GetItemsInfo
68         GetItemsLocationInfo
69         GetHostItemsInfo
70         GetItemnumbersForBiblio
71         get_itemnumbers_of
72         get_hostitemnumbers_of
73         GetItemnumberFromBarcode
74         GetBarcodeFromItemnumber
75         GetHiddenItemnumbers
76                 DelItemCheck
77                 MoveItemFromBiblio 
78                 GetLatestAcquisitions
79         CartToShelf
80
81         GetAnalyticsCount
82         GetItemHolds
83
84         SearchItems
85
86         PrepareItemrecordDisplay
87
88     );
89 }
90
91 =head1 NAME
92
93 C4::Items - item management functions
94
95 =head1 DESCRIPTION
96
97 This module contains an API for manipulating item 
98 records in Koha, and is used by cataloguing, circulation,
99 acquisitions, and serials management.
100
101 A Koha item record is stored in two places: the
102 items table and embedded in a MARC tag in the XML
103 version of the associated bib record in C<biblioitems.marcxml>.
104 This is done to allow the item information to be readily
105 indexed (e.g., by Zebra), but means that each item
106 modification transaction must keep the items table
107 and the MARC XML in sync at all times.
108
109 Consequently, all code that creates, modifies, or deletes
110 item records B<must> use an appropriate function from 
111 C<C4::Items>.  If no existing function is suitable, it is
112 better to add one to C<C4::Items> than to use add
113 one-off SQL statements to add or modify items.
114
115 The items table will be considered authoritative.  In other
116 words, if there is ever a discrepancy between the items
117 table and the MARC XML, the items table should be considered
118 accurate.
119
120 =head1 HISTORICAL NOTE
121
122 Most of the functions in C<C4::Items> were originally in
123 the C<C4::Biblio> module.
124
125 =head1 CORE EXPORTED FUNCTIONS
126
127 The following functions are meant for use by users
128 of C<C4::Items>
129
130 =cut
131
132 =head2 GetItem
133
134   $item = GetItem($itemnumber,$barcode,$serial);
135
136 Return item information, for a given itemnumber or barcode.
137 The return value is a hashref mapping item column
138 names to values.  If C<$serial> is true, include serial publication data.
139
140 =cut
141
142 sub GetItem {
143     my ($itemnumber,$barcode, $serial) = @_;
144     my $dbh = C4::Context->dbh;
145         my $data;
146     if ($itemnumber) {
147         my $sth = $dbh->prepare("
148             SELECT * FROM items 
149             WHERE itemnumber = ?");
150         $sth->execute($itemnumber);
151         $data = $sth->fetchrow_hashref;
152     } else {
153         my $sth = $dbh->prepare("
154             SELECT * FROM items 
155             WHERE barcode = ?"
156             );
157         $sth->execute($barcode);                
158         $data = $sth->fetchrow_hashref;
159     }
160     if ( $serial) {      
161     my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
162         $ssth->execute($data->{'itemnumber'}) ;
163         ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
164     }
165         #if we don't have an items.itype, use biblioitems.itemtype.
166         if( ! $data->{'itype'} ) {
167                 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems  WHERE biblionumber = ?");
168                 $sth->execute($data->{'biblionumber'});
169                 ($data->{'itype'}) = $sth->fetchrow_array;
170         }
171     return $data;
172 }    # sub GetItem
173
174 =head2 CartToShelf
175
176   CartToShelf($itemnumber);
177
178 Set the current shelving location of the item record
179 to its stored permanent shelving location.  This is
180 primarily used to indicate when an item whose current
181 location is a special processing ('PROC') or shelving cart
182 ('CART') location is back in the stacks.
183
184 =cut
185
186 sub CartToShelf {
187     my ( $itemnumber ) = @_;
188
189     unless ( $itemnumber ) {
190         croak "FAILED CartToShelf() - no itemnumber supplied";
191     }
192
193     my $item = GetItem($itemnumber);
194     $item->{location} = $item->{permanent_location};
195     ModItem($item, undef, $itemnumber);
196 }
197
198 =head2 AddItemFromMarc
199
200   my ($biblionumber, $biblioitemnumber, $itemnumber) 
201       = AddItemFromMarc($source_item_marc, $biblionumber);
202
203 Given a MARC::Record object containing an embedded item
204 record and a biblionumber, create a new item record.
205
206 =cut
207
208 sub AddItemFromMarc {
209     my ( $source_item_marc, $biblionumber ) = @_;
210     my $dbh = C4::Context->dbh;
211
212     # parse item hash from MARC
213     my $frameworkcode = GetFrameworkCode( $biblionumber );
214         my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
215         
216         my $localitemmarc=MARC::Record->new;
217         $localitemmarc->append_fields($source_item_marc->field($itemtag));
218     my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
219     my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
220     return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
221 }
222
223 =head2 AddItem
224
225   my ($biblionumber, $biblioitemnumber, $itemnumber) 
226       = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
227
228 Given a hash containing item column names as keys,
229 create a new Koha item record.
230
231 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
232 do not need to be supplied for general use; they exist
233 simply to allow them to be picked up from AddItemFromMarc.
234
235 The final optional parameter, C<$unlinked_item_subfields>, contains
236 an arrayref containing subfields present in the original MARC
237 representation of the item (e.g., from the item editor) that are
238 not mapped to C<items> columns directly but should instead
239 be stored in C<items.more_subfields_xml> and included in 
240 the biblio items tag for display and indexing.
241
242 =cut
243
244 sub AddItem {
245     my $item = shift;
246     my $biblionumber = shift;
247
248     my $dbh           = @_ ? shift : C4::Context->dbh;
249     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
250     my $unlinked_item_subfields;  
251     if (@_) {
252         $unlinked_item_subfields = shift
253     };
254
255     # needs old biblionumber and biblioitemnumber
256     $item->{'biblionumber'} = $biblionumber;
257     my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
258     $sth->execute( $item->{'biblionumber'} );
259     ($item->{'biblioitemnumber'}) = $sth->fetchrow;
260
261     _set_defaults_for_add($item);
262     _set_derived_columns_for_add($item);
263     $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
264     # FIXME - checks here
265     unless ( $item->{itype} ) {  # default to biblioitem.itemtype if no itype
266         my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
267         $itype_sth->execute( $item->{'biblionumber'} );
268         ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
269     }
270
271         my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
272     $item->{'itemnumber'} = $itemnumber;
273
274     ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver", undef, undef );
275    
276     logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
277     
278     return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
279 }
280
281 =head2 AddItemBatchFromMarc
282
283   ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, 
284              $biblionumber, $biblioitemnumber, $frameworkcode);
285
286 Efficiently create item records from a MARC biblio record with
287 embedded item fields.  This routine is suitable for batch jobs.
288
289 This API assumes that the bib record has already been
290 saved to the C<biblio> and C<biblioitems> tables.  It does
291 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
292 are populated, but it will do so via a call to ModBibiloMarc.
293
294 The goal of this API is to have a similar effect to using AddBiblio
295 and AddItems in succession, but without inefficient repeated
296 parsing of the MARC XML bib record.
297
298 This function returns an arrayref of new itemsnumbers and an arrayref of item
299 errors encountered during the processing.  Each entry in the errors
300 list is a hashref containing the following keys:
301
302 =over
303
304 =item item_sequence
305
306 Sequence number of original item tag in the MARC record.
307
308 =item item_barcode
309
310 Item barcode, provide to assist in the construction of
311 useful error messages.
312
313 =item error_code
314
315 Code representing the error condition.  Can be 'duplicate_barcode',
316 'invalid_homebranch', or 'invalid_holdingbranch'.
317
318 =item error_information
319
320 Additional information appropriate to the error condition.
321
322 =back
323
324 =cut
325
326 sub AddItemBatchFromMarc {
327     my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
328     my $error;
329     my @itemnumbers = ();
330     my @errors = ();
331     my $dbh = C4::Context->dbh;
332
333     # We modify the record, so lets work on a clone so we don't change the
334     # original.
335     $record = $record->clone();
336     # loop through the item tags and start creating items
337     my @bad_item_fields = ();
338     my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
339     my $item_sequence_num = 0;
340     ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
341         $item_sequence_num++;
342         # we take the item field and stick it into a new
343         # MARC record -- this is required so far because (FIXME)
344         # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
345         # and there is no TransformMarcFieldToKoha
346         my $temp_item_marc = MARC::Record->new();
347         $temp_item_marc->append_fields($item_field);
348     
349         # add biblionumber and biblioitemnumber
350         my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
351         my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
352         $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
353         $item->{'biblionumber'} = $biblionumber;
354         $item->{'biblioitemnumber'} = $biblioitemnumber;
355
356         # check for duplicate barcode
357         my %item_errors = CheckItemPreSave($item);
358         if (%item_errors) {
359             push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
360             push @bad_item_fields, $item_field;
361             next ITEMFIELD;
362         }
363
364         _set_defaults_for_add($item);
365         _set_derived_columns_for_add($item);
366         my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
367         warn $error if $error;
368         push @itemnumbers, $itemnumber; # FIXME not checking error
369         $item->{'itemnumber'} = $itemnumber;
370
371         logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog"); 
372
373         my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
374         $item_field->replace_with($new_item_marc->field($itemtag));
375     }
376
377     # remove any MARC item fields for rejected items
378     foreach my $item_field (@bad_item_fields) {
379         $record->delete_field($item_field);
380     }
381
382     # update the MARC biblio
383  #   $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
384
385     return (\@itemnumbers, \@errors);
386 }
387
388 =head2 ModItemFromMarc
389
390   ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
391
392 This function updates an item record based on a supplied
393 C<MARC::Record> object containing an embedded item field.
394 This API is meant for the use of C<additem.pl>; for 
395 other purposes, C<ModItem> should be used.
396
397 This function uses the hash %default_values_for_mod_from_marc,
398 which contains default values for item fields to
399 apply when modifying an item.  This is needed beccause
400 if an item field's value is cleared, TransformMarcToKoha
401 does not include the column in the
402 hash that's passed to ModItem, which without
403 use of this hash makes it impossible to clear
404 an item field's value.  See bug 2466.
405
406 Note that only columns that can be directly
407 changed from the cataloging and serials
408 item editors are included in this hash.
409
410 Returns item record
411
412 =cut
413
414 my %default_values_for_mod_from_marc = (
415     barcode              => undef, 
416     booksellerid         => undef, 
417     ccode                => undef, 
418     'items.cn_source'    => undef, 
419     copynumber           => undef, 
420     damaged              => 0,
421 #    dateaccessioned      => undef,
422     enumchron            => undef, 
423     holdingbranch        => undef, 
424     homebranch           => undef, 
425     itemcallnumber       => undef, 
426     itemlost             => 0,
427     itemnotes            => undef, 
428     itype                => undef, 
429     location             => undef, 
430     permanent_location   => undef,
431     materials            => undef, 
432     notforloan           => 0,
433     paidfor              => undef, 
434     price                => undef, 
435     replacementprice     => undef, 
436     replacementpricedate => undef, 
437     restricted           => undef, 
438     stack                => undef, 
439     stocknumber          => undef, 
440     uri                  => undef, 
441     wthdrawn             => 0,
442 );
443
444 sub ModItemFromMarc {
445     my $item_marc = shift;
446     my $biblionumber = shift;
447     my $itemnumber = shift;
448
449     my $dbh           = C4::Context->dbh;
450     my $frameworkcode = GetFrameworkCode($biblionumber);
451     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
452
453     my $localitemmarc = MARC::Record->new;
454     $localitemmarc->append_fields( $item_marc->field($itemtag) );
455     my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
456     foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
457         $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
458     }
459     my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
460
461     ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields); 
462     return $item;
463 }
464
465 =head2 ModItem
466
467   ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
468
469 Change one or more columns in an item record and update
470 the MARC representation of the item.
471
472 The first argument is a hashref mapping from item column
473 names to the new values.  The second and third arguments
474 are the biblionumber and itemnumber, respectively.
475
476 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
477 an arrayref containing subfields present in the original MARC
478 representation of the item (e.g., from the item editor) that are
479 not mapped to C<items> columns directly but should instead
480 be stored in C<items.more_subfields_xml> and included in 
481 the biblio items tag for display and indexing.
482
483 If one of the changed columns is used to calculate
484 the derived value of a column such as C<items.cn_sort>, 
485 this routine will perform the necessary calculation
486 and set the value.
487
488 =cut
489
490 sub ModItem {
491     my $item = shift;
492     my $biblionumber = shift;
493     my $itemnumber = shift;
494
495     # if $biblionumber is undefined, get it from the current item
496     unless (defined $biblionumber) {
497         $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
498     }
499
500     my $dbh           = @_ ? shift : C4::Context->dbh;
501     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
502     
503     my $unlinked_item_subfields;  
504     if (@_) {
505         $unlinked_item_subfields = shift;
506         $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
507     };
508
509     $item->{'itemnumber'} = $itemnumber or return undef;
510
511     $item->{onloan} = undef if $item->{itemlost};
512
513     _set_derived_columns_for_mod($item);
514     _do_column_fixes_for_mod($item);
515     # FIXME add checks
516     # duplicate barcode
517     # attempt to change itemnumber
518     # attempt to change biblionumber (if we want
519     # an API to relink an item to a different bib,
520     # it should be a separate function)
521
522     # update items table
523     _koha_modify_item($item);
524
525     # request that bib be reindexed so that searching on current
526     # item status is possible
527     ModZebra( $biblionumber, "specialUpdate", "biblioserver", undef, undef );
528
529     logaction("CATALOGUING", "MODIFY", $itemnumber, Dumper($item)) if C4::Context->preference("CataloguingLog");
530 }
531
532 =head2 ModItemTransfer
533
534   ModItemTransfer($itenumber, $frombranch, $tobranch);
535
536 Marks an item as being transferred from one branch
537 to another.
538
539 =cut
540
541 sub ModItemTransfer {
542     my ( $itemnumber, $frombranch, $tobranch ) = @_;
543
544     my $dbh = C4::Context->dbh;
545
546     #new entry in branchtransfers....
547     my $sth = $dbh->prepare(
548         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
549         VALUES (?, ?, NOW(), ?)");
550     $sth->execute($itemnumber, $frombranch, $tobranch);
551
552     ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
553     ModDateLastSeen($itemnumber);
554     return;
555 }
556
557 =head2 ModDateLastSeen
558
559   ModDateLastSeen($itemnum);
560
561 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
562 C<$itemnum> is the item number
563
564 =cut
565
566 sub ModDateLastSeen {
567     my ($itemnumber) = @_;
568     
569     my $today = C4::Dates->new();    
570     ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
571 }
572
573 =head2 DelItem
574
575   DelItem($dbh, $biblionumber, $itemnumber);
576
577 Exported function (core API) for deleting an item record in Koha.
578
579 =cut
580
581 sub DelItem {
582     my ( $dbh, $biblionumber, $itemnumber ) = @_;
583     
584     # FIXME check the item has no current issues
585     
586     _koha_delete_item( $dbh, $itemnumber );
587
588     # get the MARC record
589     my $record = GetMarcBiblio($biblionumber);
590     ModZebra( $biblionumber, "specialUpdate", "biblioserver", undef, undef );
591
592     # backup the record
593     my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
594     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
595     # This last update statement makes that the timestamp column in deleteditems is updated too. If you remove these lines, please add a line to update the timestamp separately. See Bugzilla report 7146 and Biblio.pm (DelBiblio).
596
597     #search item field code
598     logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
599 }
600
601 =head2 CheckItemPreSave
602
603     my $item_ref = TransformMarcToKoha($marc, 'items');
604     # do stuff
605     my %errors = CheckItemPreSave($item_ref);
606     if (exists $errors{'duplicate_barcode'}) {
607         print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
608     } elsif (exists $errors{'invalid_homebranch'}) {
609         print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
610     } elsif (exists $errors{'invalid_holdingbranch'}) {
611         print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
612     } else {
613         print "item is OK";
614     }
615
616 Given a hashref containing item fields, determine if it can be
617 inserted or updated in the database.  Specifically, checks for
618 database integrity issues, and returns a hash containing any
619 of the following keys, if applicable.
620
621 =over 2
622
623 =item duplicate_barcode
624
625 Barcode, if it duplicates one already found in the database.
626
627 =item invalid_homebranch
628
629 Home branch, if not defined in branches table.
630
631 =item invalid_holdingbranch
632
633 Holding branch, if not defined in branches table.
634
635 =back
636
637 This function does NOT implement any policy-related checks,
638 e.g., whether current operator is allowed to save an
639 item that has a given branch code.
640
641 =cut
642
643 sub CheckItemPreSave {
644     my $item_ref = shift;
645     require C4::Branch;
646
647     my %errors = ();
648
649     # check for duplicate barcode
650     if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
651         my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
652         if ($existing_itemnumber) {
653             if (!exists $item_ref->{'itemnumber'}                       # new item
654                 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
655                 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
656             }
657         }
658     }
659
660     # check for valid home branch
661     if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
662         my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'});
663         unless (defined $branch_name) {
664             # relies on fact that branches.branchname is a non-NULL column,
665             # so GetBranchName returns undef only if branch does not exist
666             $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
667         }
668     }
669
670     # check for valid holding branch
671     if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
672         my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'});
673         unless (defined $branch_name) {
674             # relies on fact that branches.branchname is a non-NULL column,
675             # so GetBranchName returns undef only if branch does not exist
676             $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
677         }
678     }
679
680     return %errors;
681
682 }
683
684 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
685
686 The following functions provide various ways of 
687 getting an item record, a set of item records, or
688 lists of authorized values for certain item fields.
689
690 Some of the functions in this group are candidates
691 for refactoring -- for example, some of the code
692 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
693 has copy-and-paste work.
694
695 =cut
696
697 =head2 GetItemStatus
698
699   $itemstatushash = GetItemStatus($fwkcode);
700
701 Returns a list of valid values for the
702 C<items.notforloan> field.
703
704 NOTE: does B<not> return an individual item's
705 status.
706
707 Can be MARC dependant.
708 fwkcode is optional.
709 But basically could be can be loan or not
710 Create a status selector with the following code
711
712 =head3 in PERL SCRIPT
713
714  my $itemstatushash = getitemstatus;
715  my @itemstatusloop;
716  foreach my $thisstatus (keys %$itemstatushash) {
717      my %row =(value => $thisstatus,
718                  statusname => $itemstatushash->{$thisstatus}->{'statusname'},
719              );
720      push @itemstatusloop, \%row;
721  }
722  $template->param(statusloop=>\@itemstatusloop);
723
724 =head3 in TEMPLATE
725
726  <select name="statusloop">
727      <option value="">Default</option>
728  <!-- TMPL_LOOP name="statusloop" -->
729      <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
730  <!-- /TMPL_LOOP -->
731  </select>
732
733 =cut
734
735 sub GetItemStatus {
736
737     # returns a reference to a hash of references to status...
738     my ($fwk) = @_;
739     my %itemstatus;
740     my $dbh = C4::Context->dbh;
741     my $sth;
742     $fwk = '' unless ($fwk);
743     my ( $tag, $subfield ) =
744       GetMarcFromKohaField( "items.notforloan", $fwk );
745     if ( $tag and $subfield ) {
746         my $sth =
747           $dbh->prepare(
748             "SELECT authorised_value
749             FROM marc_subfield_structure
750             WHERE tagfield=?
751                 AND tagsubfield=?
752                 AND frameworkcode=?
753             "
754           );
755         $sth->execute( $tag, $subfield, $fwk );
756         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
757             my $authvalsth =
758               $dbh->prepare(
759                 "SELECT authorised_value,lib
760                 FROM authorised_values 
761                 WHERE category=? 
762                 ORDER BY lib
763                 "
764               );
765             $authvalsth->execute($authorisedvaluecat);
766             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
767                 $itemstatus{$authorisedvalue} = $lib;
768             }
769             return \%itemstatus;
770             exit 1;
771         }
772         else {
773
774             #No authvalue list
775             # build default
776         }
777     }
778
779     #No authvalue list
780     #build default
781     $itemstatus{"1"} = "Not For Loan";
782     return \%itemstatus;
783 }
784
785 =head2 GetItemLocation
786
787   $itemlochash = GetItemLocation($fwk);
788
789 Returns a list of valid values for the
790 C<items.location> field.
791
792 NOTE: does B<not> return an individual item's
793 location.
794
795 where fwk stands for an optional framework code.
796 Create a location selector with the following code
797
798 =head3 in PERL SCRIPT
799
800   my $itemlochash = getitemlocation;
801   my @itemlocloop;
802   foreach my $thisloc (keys %$itemlochash) {
803       my $selected = 1 if $thisbranch eq $branch;
804       my %row =(locval => $thisloc,
805                   selected => $selected,
806                   locname => $itemlochash->{$thisloc},
807                );
808       push @itemlocloop, \%row;
809   }
810   $template->param(itemlocationloop => \@itemlocloop);
811
812 =head3 in TEMPLATE
813
814   <select name="location">
815       <option value="">Default</option>
816   <!-- TMPL_LOOP name="itemlocationloop" -->
817       <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
818   <!-- /TMPL_LOOP -->
819   </select>
820
821 =cut
822
823 sub GetItemLocation {
824
825     # returns a reference to a hash of references to location...
826     my ($fwk) = @_;
827     my %itemlocation;
828     my $dbh = C4::Context->dbh;
829     my $sth;
830     $fwk = '' unless ($fwk);
831     my ( $tag, $subfield ) =
832       GetMarcFromKohaField( "items.location", $fwk );
833     if ( $tag and $subfield ) {
834         my $sth =
835           $dbh->prepare(
836             "SELECT authorised_value
837             FROM marc_subfield_structure 
838             WHERE tagfield=? 
839                 AND tagsubfield=? 
840                 AND frameworkcode=?"
841           );
842         $sth->execute( $tag, $subfield, $fwk );
843         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
844             my $authvalsth =
845               $dbh->prepare(
846                 "SELECT authorised_value,lib
847                 FROM authorised_values
848                 WHERE category=?
849                 ORDER BY lib"
850               );
851             $authvalsth->execute($authorisedvaluecat);
852             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
853                 $itemlocation{$authorisedvalue} = $lib;
854             }
855             return \%itemlocation;
856             exit 1;
857         }
858         else {
859
860             #No authvalue list
861             # build default
862         }
863     }
864
865     #No authvalue list
866     #build default
867     $itemlocation{"1"} = "Not For Loan";
868     return \%itemlocation;
869 }
870
871 =head2 GetLostItems
872
873   $items = GetLostItems( $where, $orderby );
874
875 This function gets a list of lost items.
876
877 =over 2
878
879 =item input:
880
881 C<$where> is a hashref. it containts a field of the items table as key
882 and the value to match as value. For example:
883
884 { barcode    => 'abc123',
885   homebranch => 'CPL',    }
886
887 C<$orderby> is a field of the items table by which the resultset
888 should be orderd.
889
890 =item return:
891
892 C<$items> is a reference to an array full of hashrefs with columns
893 from the "items" table as keys.
894
895 =item usage in the perl script:
896
897   my $where = { barcode => '0001548' };
898   my $items = GetLostItems( $where, "homebranch" );
899   $template->param( itemsloop => $items );
900
901 =back
902
903 =cut
904
905 sub GetLostItems {
906     # Getting input args.
907     my $where   = shift;
908     my $orderby = shift;
909     my $dbh     = C4::Context->dbh;
910
911     my $query   = "
912         SELECT *
913         FROM   items
914             LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
915             LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
916             LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
917         WHERE
918                 authorised_values.category = 'LOST'
919                 AND itemlost IS NOT NULL
920                 AND itemlost <> 0
921     ";
922     my @query_parameters;
923     foreach my $key (keys %$where) {
924         $query .= " AND $key LIKE ?";
925         push @query_parameters, "%$where->{$key}%";
926     }
927     my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
928     
929     if ( defined $orderby && grep($orderby, @ordervalues)) {
930         $query .= ' ORDER BY '.$orderby;
931     }
932
933     my $sth = $dbh->prepare($query);
934     $sth->execute( @query_parameters );
935     my $items = [];
936     while ( my $row = $sth->fetchrow_hashref ){
937         push @$items, $row;
938     }
939     return $items;
940 }
941
942 =head2 GetItemsForInventory
943
944   $itemlist = GetItemsForInventory($minlocation, $maxlocation, 
945                  $location, $itemtype $datelastseen, $branch, 
946                  $offset, $size, $statushash);
947
948 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
949
950 The sub returns a reference to a list of hashes, each containing
951 itemnumber, author, title, barcode, item callnumber, and date last
952 seen. It is ordered by callnumber then title.
953
954 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
955 the datelastseen can be used to specify that you want to see items not seen since a past date only.
956 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
957 $statushash requires a hashref that has the authorized values fieldname (intems.notforloan, etc...) as keys, and an arrayref of statuscodes we are searching for as values.
958
959 =cut
960
961 sub GetItemsForInventory {
962     my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
963     my $dbh = C4::Context->dbh;
964     my ( @bind_params, @where_strings );
965
966     my $query = <<'END_SQL';
967 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
968 FROM items
969   LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
970   LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
971 END_SQL
972     if ($statushash){
973         for my $authvfield (keys %$statushash){
974             if ( scalar @{$statushash->{$authvfield}} > 0 ){
975                 my $joinedvals = join ',', @{$statushash->{$authvfield}};
976                 push @where_strings, "$authvfield in (" . $joinedvals . ")";
977             }
978         }
979     }
980
981     if ($minlocation) {
982         push @where_strings, 'itemcallnumber >= ?';
983         push @bind_params, $minlocation;
984     }
985
986     if ($maxlocation) {
987         push @where_strings, 'itemcallnumber <= ?';
988         push @bind_params, $maxlocation;
989     }
990
991     if ($datelastseen) {
992         $datelastseen = format_date_in_iso($datelastseen);  
993         push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
994         push @bind_params, $datelastseen;
995     }
996
997     if ( $location ) {
998         push @where_strings, 'items.location = ?';
999         push @bind_params, $location;
1000     }
1001
1002     if ( $branchcode ) {
1003         if($branch eq "homebranch"){
1004         push @where_strings, 'items.homebranch = ?';
1005         }else{
1006             push @where_strings, 'items.holdingbranch = ?';
1007         }
1008         push @bind_params, $branchcode;
1009     }
1010     
1011     if ( $itemtype ) {
1012         push @where_strings, 'biblioitems.itemtype = ?';
1013         push @bind_params, $itemtype;
1014     }
1015
1016     if ( $ignoreissued) {
1017         $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1018         push @where_strings, 'issues.date_due IS NULL';
1019     }
1020
1021     if ( @where_strings ) {
1022         $query .= 'WHERE ';
1023         $query .= join ' AND ', @where_strings;
1024     }
1025     $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1026     my $sth = $dbh->prepare($query);
1027     $sth->execute( @bind_params );
1028
1029     my @results;
1030     $size--;
1031     while ( my $row = $sth->fetchrow_hashref ) {
1032         $offset-- if ($offset);
1033         $row->{datelastseen}=format_date($row->{datelastseen});
1034         if ( ( !$offset ) && $size ) {
1035             push @results, $row;
1036             $size--;
1037         }
1038     }
1039     return \@results;
1040 }
1041
1042 =head2 GetItemsCount
1043
1044   $count = &GetItemsCount( $biblionumber);
1045
1046 This function return count of item with $biblionumber
1047
1048 =cut
1049
1050 sub GetItemsCount {
1051     my ( $biblionumber ) = @_;
1052     my $dbh = C4::Context->dbh;
1053     my $query = "SELECT count(*)
1054           FROM  items 
1055           WHERE biblionumber=?";
1056     my $sth = $dbh->prepare($query);
1057     $sth->execute($biblionumber);
1058     my $count = $sth->fetchrow;  
1059     return ($count);
1060 }
1061
1062 =head2 GetItemInfosOf
1063
1064   GetItemInfosOf(@itemnumbers);
1065
1066 =cut
1067
1068 sub GetItemInfosOf {
1069     my @itemnumbers = @_;
1070
1071     my $query = '
1072         SELECT *
1073         FROM items
1074         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1075     ';
1076     return get_infos_of( $query, 'itemnumber' );
1077 }
1078
1079 =head2 GetItemsByBiblioitemnumber
1080
1081   GetItemsByBiblioitemnumber($biblioitemnumber);
1082
1083 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1084 Called by C<C4::XISBN>
1085
1086 =cut
1087
1088 sub GetItemsByBiblioitemnumber {
1089     my ( $bibitem ) = @_;
1090     my $dbh = C4::Context->dbh;
1091     my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1092     # Get all items attached to a biblioitem
1093     my $i = 0;
1094     my @results; 
1095     $sth->execute($bibitem) || die $sth->errstr;
1096     while ( my $data = $sth->fetchrow_hashref ) {  
1097         # Foreach item, get circulation information
1098         my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1099                                    WHERE itemnumber = ?
1100                                    AND issues.borrowernumber = borrowers.borrowernumber"
1101         );
1102         $sth2->execute( $data->{'itemnumber'} );
1103         if ( my $data2 = $sth2->fetchrow_hashref ) {
1104             # if item is out, set the due date and who it is out too
1105             $data->{'date_due'}   = $data2->{'date_due'};
1106             $data->{'cardnumber'} = $data2->{'cardnumber'};
1107             $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
1108         }
1109         else {
1110             # set date_due to blank, so in the template we check itemlost, and wthdrawn 
1111             $data->{'date_due'} = '';                                                                                                         
1112         }    # else         
1113         # Find the last 3 people who borrowed this item.                  
1114         my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1115                       AND old_issues.borrowernumber = borrowers.borrowernumber
1116                       ORDER BY returndate desc,timestamp desc LIMIT 3";
1117         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1118         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1119         my $i2 = 0;
1120         while ( my $data2 = $sth2->fetchrow_hashref ) {
1121             $data->{"timestamp$i2"} = $data2->{'timestamp'};
1122             $data->{"card$i2"}      = $data2->{'cardnumber'};
1123             $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
1124             $i2++;
1125         }
1126         push(@results,$data);
1127     } 
1128     return (\@results); 
1129 }
1130
1131 =head2 GetItemsInfo
1132
1133   @results = GetItemsInfo($biblionumber);
1134
1135 Returns information about items with the given biblionumber.
1136
1137 C<GetItemsInfo> returns a list of references-to-hash. Each element
1138 contains a number of keys. Most of them are attributes from the
1139 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1140 Koha database. Other keys include:
1141
1142 =over 2
1143
1144 =item C<$data-E<gt>{branchname}>
1145
1146 The name (not the code) of the branch to which the book belongs.
1147
1148 =item C<$data-E<gt>{datelastseen}>
1149
1150 This is simply C<items.datelastseen>, except that while the date is
1151 stored in YYYY-MM-DD format in the database, here it is converted to
1152 DD/MM/YYYY format. A NULL date is returned as C<//>.
1153
1154 =item C<$data-E<gt>{datedue}>
1155
1156 =item C<$data-E<gt>{class}>
1157
1158 This is the concatenation of C<biblioitems.classification>, the book's
1159 Dewey code, and C<biblioitems.subclass>.
1160
1161 =item C<$data-E<gt>{ocount}>
1162
1163 I think this is the number of copies of the book available.
1164
1165 =item C<$data-E<gt>{order}>
1166
1167 If this is set, it is set to C<One Order>.
1168
1169 =back
1170
1171 =cut
1172
1173 sub GetItemsInfo {
1174     my ( $biblionumber ) = @_;
1175     my $dbh   = C4::Context->dbh;
1176     # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1177     my $query = "
1178     SELECT items.*,
1179            biblio.*,
1180            biblioitems.volume,
1181            biblioitems.number,
1182            biblioitems.itemtype,
1183            biblioitems.isbn,
1184            biblioitems.issn,
1185            biblioitems.publicationyear,
1186            biblioitems.publishercode,
1187            biblioitems.volumedate,
1188            biblioitems.volumedesc,
1189            biblioitems.lccn,
1190            biblioitems.url,
1191            items.notforloan as itemnotforloan,
1192            itemtypes.description,
1193            itemtypes.notforloan as notforloan_per_itemtype,
1194            holding.branchurl,
1195            holding.branchname,
1196            holding.opac_info as branch_opac_info
1197      FROM items
1198      LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1199      LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1200      LEFT JOIN biblio      ON      biblio.biblionumber     = items.biblionumber
1201      LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1202      LEFT JOIN itemtypes   ON   itemtypes.itemtype         = "
1203      . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1204     $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1205     my $sth = $dbh->prepare($query);
1206     $sth->execute($biblionumber);
1207     my $i = 0;
1208     my @results;
1209     my $serial;
1210
1211     my $isth    = $dbh->prepare(
1212         "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1213         FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1214         WHERE  itemnumber = ?"
1215        );
1216         my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); 
1217         while ( my $data = $sth->fetchrow_hashref ) {
1218         my $datedue = '';
1219         $isth->execute( $data->{'itemnumber'} );
1220         if ( my $idata = $isth->fetchrow_hashref ) {
1221             $data->{borrowernumber} = $idata->{borrowernumber};
1222             $data->{cardnumber}     = $idata->{cardnumber};
1223             $data->{surname}     = $idata->{surname};
1224             $data->{firstname}     = $idata->{firstname};
1225             $data->{lastreneweddate} = $idata->{lastreneweddate};
1226             $datedue                = $idata->{'date_due'};
1227         if (C4::Context->preference("IndependantBranches")){
1228         my $userenv = C4::Context->userenv;
1229         if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { 
1230             $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1231         }
1232         }
1233         }
1234                 if ( $data->{'serial'}) {       
1235                         $ssth->execute($data->{'itemnumber'}) ;
1236                         ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1237                         $serial = 1;
1238         }
1239         #get branch information.....
1240         my $bsth = $dbh->prepare(
1241             "SELECT * FROM branches WHERE branchcode = ?
1242         "
1243         );
1244         $bsth->execute( $data->{'holdingbranch'} );
1245         if ( my $bdata = $bsth->fetchrow_hashref ) {
1246             $data->{'branchname'} = $bdata->{'branchname'};
1247         }
1248         $data->{'datedue'}        = $datedue;
1249
1250         # get notforloan complete status if applicable
1251         my $sthnflstatus = $dbh->prepare(
1252             'SELECT authorised_value
1253             FROM   marc_subfield_structure
1254             WHERE  kohafield="items.notforloan"
1255         '
1256         );
1257
1258         $sthnflstatus->execute;
1259         my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1260         if ($authorised_valuecode) {
1261             $sthnflstatus = $dbh->prepare(
1262                 "SELECT lib FROM authorised_values
1263                  WHERE  category=?
1264                  AND authorised_value=?"
1265             );
1266             $sthnflstatus->execute( $authorised_valuecode,
1267                 $data->{itemnotforloan} );
1268             my ($lib) = $sthnflstatus->fetchrow;
1269             $data->{notforloanvalue} = $lib;
1270         }
1271
1272         # get restricted status and description if applicable
1273         my $restrictedstatus = $dbh->prepare(
1274             'SELECT authorised_value
1275             FROM   marc_subfield_structure
1276             WHERE  kohafield="items.restricted"
1277         '
1278         );
1279
1280         $restrictedstatus->execute;
1281         ($authorised_valuecode) = $restrictedstatus->fetchrow;
1282         if ($authorised_valuecode) {
1283             $restrictedstatus = $dbh->prepare(
1284                 "SELECT lib,lib_opac FROM authorised_values
1285                  WHERE  category=?
1286                  AND authorised_value=?"
1287             );
1288             $restrictedstatus->execute( $authorised_valuecode,
1289                 $data->{restricted} );
1290
1291             if ( my $rstdata = $restrictedstatus->fetchrow_hashref ) {
1292                 $data->{restricted} = $rstdata->{'lib'};
1293                 $data->{restrictedopac} = $rstdata->{'lib_opac'};
1294             }
1295         }
1296
1297         # my stack procedures
1298         my $stackstatus = $dbh->prepare(
1299             'SELECT authorised_value
1300              FROM   marc_subfield_structure
1301              WHERE  kohafield="items.stack"
1302         '
1303         );
1304         $stackstatus->execute;
1305
1306         ($authorised_valuecode) = $stackstatus->fetchrow;
1307         if ($authorised_valuecode) {
1308             $stackstatus = $dbh->prepare(
1309                 "SELECT lib
1310                  FROM   authorised_values
1311                  WHERE  category=?
1312                  AND    authorised_value=?
1313             "
1314             );
1315             $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1316             my ($lib) = $stackstatus->fetchrow;
1317             $data->{stack} = $lib;
1318         }
1319         # Find the last 3 people who borrowed this item.
1320         my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1321                                     WHERE itemnumber = ?
1322                                     AND old_issues.borrowernumber = borrowers.borrowernumber
1323                                     ORDER BY returndate DESC
1324                                     LIMIT 3");
1325         $sth2->execute($data->{'itemnumber'});
1326         my $ii = 0;
1327         while (my $data2 = $sth2->fetchrow_hashref()) {
1328             $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1329             $data->{"card$ii"}      = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1330             $data->{"borrower$ii"}  = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1331             $ii++;
1332         }
1333
1334         $results[$i] = $data;
1335         $i++;
1336     }
1337         if($serial) {
1338                 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1339         } else {
1340         return (@results);
1341         }
1342 }
1343
1344 =head2 GetItemsLocationInfo
1345
1346   my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1347
1348 Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1349
1350 C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1351
1352 =over 2
1353
1354 =item C<$data-E<gt>{homebranch}>
1355
1356 Branch Name of the item's homebranch
1357
1358 =item C<$data-E<gt>{holdingbranch}>
1359
1360 Branch Name of the item's holdingbranch
1361
1362 =item C<$data-E<gt>{location}>
1363
1364 Item's shelving location code
1365
1366 =item C<$data-E<gt>{location_intranet}>
1367
1368 The intranet description for the Shelving Location as set in authorised_values 'LOC'
1369
1370 =item C<$data-E<gt>{location_opac}>
1371
1372 The OPAC description for the Shelving Location as set in authorised_values 'LOC'.  Falls back to intranet description if no OPAC 
1373 description is set.
1374
1375 =item C<$data-E<gt>{itemcallnumber}>
1376
1377 Item's itemcallnumber
1378
1379 =item C<$data-E<gt>{cn_sort}>
1380
1381 Item's call number normalized for sorting
1382
1383 =back
1384   
1385 =cut
1386
1387 sub GetItemsLocationInfo {
1388         my $biblionumber = shift;
1389         my @results;
1390
1391         my $dbh = C4::Context->dbh;
1392         my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch, 
1393                             location, itemcallnumber, cn_sort
1394                      FROM items, branches as a, branches as b
1395                      WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode 
1396                      AND biblionumber = ?
1397                      ORDER BY cn_sort ASC";
1398         my $sth = $dbh->prepare($query);
1399         $sth->execute($biblionumber);
1400
1401         while ( my $data = $sth->fetchrow_hashref ) {
1402              $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1403              $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1404              push @results, $data;
1405         }
1406         return @results;
1407 }
1408
1409 =head2 GetHostItemsInfo
1410
1411         $hostiteminfo = GetHostItemsInfo($hostfield);
1412         Returns the iteminfo for items linked to records via a host field
1413
1414 =cut
1415
1416 sub GetHostItemsInfo {
1417         my ($record) = @_;
1418         my @returnitemsInfo;
1419
1420         if (C4::Context->preference('marcflavour') eq 'MARC21' ||
1421         C4::Context->preference('marcflavour') eq 'NORMARC'){
1422             foreach my $hostfield ( $record->field('773') ) {
1423                 my $hostbiblionumber = $hostfield->subfield("0");
1424                 my $linkeditemnumber = $hostfield->subfield("9");
1425                 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1426                 foreach my $hostitemInfo (@hostitemInfos){
1427                         if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1428                                 push (@returnitemsInfo,$hostitemInfo);
1429                                 last;
1430                         }
1431                 }
1432             }
1433         } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){
1434             foreach my $hostfield ( $record->field('461') ) {
1435                 my $hostbiblionumber = $hostfield->subfield("0");
1436                 my $linkeditemnumber = $hostfield->subfield("9");
1437                 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1438                 foreach my $hostitemInfo (@hostitemInfos){
1439                         if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1440                                 push (@returnitemsInfo,$hostitemInfo);
1441                                 last;
1442                         }
1443                 }
1444             }
1445         }
1446         return @returnitemsInfo;
1447 }
1448
1449
1450 =head2 GetLastAcquisitions
1451
1452   my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 
1453                                     'itemtypes' => ('BK','BD')}, 10);
1454
1455 =cut
1456
1457 sub  GetLastAcquisitions {
1458         my ($data,$max) = @_;
1459
1460         my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1461         
1462         my $number_of_branches = @{$data->{branches}};
1463         my $number_of_itemtypes   = @{$data->{itemtypes}};
1464         
1465         
1466         my @where = ('WHERE 1 '); 
1467         $number_of_branches and push @where
1468            , 'AND holdingbranch IN (' 
1469            , join(',', ('?') x $number_of_branches )
1470            , ')'
1471          ;
1472         
1473         $number_of_itemtypes and push @where
1474            , "AND $itemtype IN (" 
1475            , join(',', ('?') x $number_of_itemtypes )
1476            , ')'
1477          ;
1478
1479         my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1480                                  FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber) 
1481                                     RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1482                                     @where
1483                                     GROUP BY biblio.biblionumber 
1484                                     ORDER BY dateaccessioned DESC LIMIT $max";
1485
1486         my $dbh = C4::Context->dbh;
1487         my $sth = $dbh->prepare($query);
1488     
1489     $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1490         
1491         my @results;
1492         while( my $row = $sth->fetchrow_hashref){
1493                 push @results, {date => $row->{dateaccessioned} 
1494                                                 , biblionumber => $row->{biblionumber}
1495                                                 , title => $row->{title}};
1496         }
1497         
1498         return @results;
1499 }
1500
1501 =head2 GetItemnumbersForBiblio
1502
1503   my $itemnumbers = GetItemnumbersForBiblio($biblionumber);
1504
1505 Given a single biblionumber, return an arrayref of all the corresponding itemnumbers
1506
1507 =cut
1508
1509 sub GetItemnumbersForBiblio {
1510     my $biblionumber = shift;
1511     my @items;
1512     my $dbh = C4::Context->dbh;
1513     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
1514     $sth->execute($biblionumber);
1515     while (my $result = $sth->fetchrow_hashref) {
1516         push @items, $result->{'itemnumber'};
1517     }
1518     return \@items;
1519 }
1520
1521 =head2 get_itemnumbers_of
1522
1523   my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1524
1525 Given a list of biblionumbers, return the list of corresponding itemnumbers
1526 for each biblionumber.
1527
1528 Return a reference on a hash where keys are biblionumbers and values are
1529 references on array of itemnumbers.
1530
1531 =cut
1532
1533 sub get_itemnumbers_of {
1534     my @biblionumbers = @_;
1535
1536     my $dbh = C4::Context->dbh;
1537
1538     my $query = '
1539         SELECT itemnumber,
1540             biblionumber
1541         FROM items
1542         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1543     ';
1544     my $sth = $dbh->prepare($query);
1545     $sth->execute(@biblionumbers);
1546
1547     my %itemnumbers_of;
1548
1549     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1550         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1551     }
1552
1553     return \%itemnumbers_of;
1554 }
1555
1556 =head2 get_hostitemnumbers_of
1557
1558   my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);
1559
1560 Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields
1561
1562 Return a reference on a hash where key is a biblionumber and values are
1563 references on array of itemnumbers.
1564
1565 =cut
1566
1567
1568 sub get_hostitemnumbers_of {
1569         my ($biblionumber) = @_;
1570         my $marcrecord = GetMarcBiblio($biblionumber);
1571         my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1572         
1573         my $marcflavor = C4::Context->preference('marcflavour');
1574         if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1575         $tag='773';
1576         $biblio_s='0';
1577         $item_s='9';
1578     } elsif ($marcflavor eq 'UNIMARC') {
1579         $tag='461';
1580         $biblio_s='0';
1581         $item_s='9';
1582     }
1583
1584     foreach my $hostfield ( $marcrecord->field($tag) ) {
1585         my $hostbiblionumber = $hostfield->subfield($biblio_s);
1586         my $linkeditemnumber = $hostfield->subfield($item_s);
1587         my @itemnumbers;
1588         if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber})
1589         {
1590             @itemnumbers = @$itemnumbers;
1591         }
1592         foreach my $itemnumber (@itemnumbers){
1593             if ($itemnumber eq $linkeditemnumber){
1594                 push (@returnhostitemnumbers,$itemnumber);
1595                 last;
1596             }
1597         }
1598     }
1599     return @returnhostitemnumbers;
1600 }
1601
1602
1603 =head2 GetItemnumberFromBarcode
1604
1605   $result = GetItemnumberFromBarcode($barcode);
1606
1607 =cut
1608
1609 sub GetItemnumberFromBarcode {
1610     my ($barcode) = @_;
1611     my $dbh = C4::Context->dbh;
1612
1613     my $rq =
1614       $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1615     $rq->execute($barcode);
1616     my ($result) = $rq->fetchrow;
1617     return ($result);
1618 }
1619
1620 =head2 GetBarcodeFromItemnumber
1621
1622   $result = GetBarcodeFromItemnumber($itemnumber);
1623
1624 =cut
1625
1626 sub GetBarcodeFromItemnumber {
1627     my ($itemnumber) = @_;
1628     my $dbh = C4::Context->dbh;
1629
1630     my $rq =
1631       $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1632     $rq->execute($itemnumber);
1633     my ($result) = $rq->fetchrow;
1634     return ($result);
1635 }
1636
1637 =head2 GetHiddenItemnumbers
1638
1639 =over 4
1640
1641 $result = GetHiddenItemnumbers(@items);
1642
1643 =back
1644
1645 =cut
1646
1647 sub GetHiddenItemnumbers {
1648     my (@items) = @_;
1649     my @resultitems;
1650
1651     my $yaml = C4::Context->preference('OpacHiddenItems');
1652     $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1653     my $hidingrules;
1654     eval {
1655         $hidingrules = YAML::Load($yaml);
1656     };
1657     if ($@) {
1658         warn "Unable to parse OpacHiddenItems syspref : $@";
1659         return ();
1660     }
1661     my $dbh = C4::Context->dbh;
1662
1663     # For each item
1664     foreach my $item (@items) {
1665
1666         # We check each rule
1667         foreach my $field (keys %$hidingrules) {
1668             my $val;
1669             if (exists $item->{$field}) {
1670                 $val = $item->{$field};
1671             }
1672             else {
1673                 my $query = "SELECT $field from items where itemnumber = ?";
1674                 $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1675             }
1676             $val = '' unless defined $val;
1677
1678             # If the results matches the values in the yaml file
1679             if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1680
1681                 # We add the itemnumber to the list
1682                 push @resultitems, $item->{'itemnumber'};
1683
1684                 # If at least one rule matched for an item, no need to test the others
1685                 last;
1686             }
1687         }
1688     }
1689     return @resultitems;
1690 }
1691
1692 =head3 get_item_authorised_values
1693
1694 find the types and values for all authorised values assigned to this item.
1695
1696 parameters: itemnumber
1697
1698 returns: a hashref malling the authorised value to the value set for this itemnumber
1699
1700     $authorised_values = {
1701              'CCODE'      => undef,
1702              'DAMAGED'    => '0',
1703              'LOC'        => '3',
1704              'LOST'       => '0'
1705              'NOT_LOAN'   => '0',
1706              'RESTRICTED' => undef,
1707              'STACK'      => undef,
1708              'WITHDRAWN'  => '0',
1709              'branches'   => 'CPL',
1710              'cn_source'  => undef,
1711              'itemtypes'  => 'SER',
1712            };
1713
1714 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1715
1716 =cut
1717
1718 sub get_item_authorised_values {
1719     my $itemnumber = shift;
1720
1721     # assume that these entries in the authorised_value table are item level.
1722     my $query = q(SELECT distinct authorised_value, kohafield
1723                     FROM marc_subfield_structure
1724                     WHERE kohafield like 'item%'
1725                       AND authorised_value != '' );
1726
1727     my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1728     my $iteminfo = GetItem( $itemnumber );
1729     # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1730     my $return;
1731     foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1732         my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1733         $field =~ s/^items\.//;
1734         if ( exists $iteminfo->{ $field } ) {
1735             $return->{ $this_authorised_value } = $iteminfo->{ $field };
1736         }
1737     }
1738     # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1739     return $return;
1740 }
1741
1742 =head3 get_authorised_value_images
1743
1744 find a list of icons that are appropriate for display based on the
1745 authorised values for a biblio.
1746
1747 parameters: listref of authorised values, such as comes from
1748 get_item_authorised_values or
1749 from C4::Biblio::get_biblio_authorised_values
1750
1751 returns: listref of hashrefs for each image. Each hashref looks like this:
1752
1753       { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1754         label    => '',
1755         category => '',
1756         value    => '', }
1757
1758 Notes: Currently, I put on the full path to the images on the staff
1759 side. This should either be configurable or not done at all. Since I
1760 have to deal with 'intranet' or 'opac' in
1761 get_biblio_authorised_values, perhaps I should be passing it in.
1762
1763 =cut
1764
1765 sub get_authorised_value_images {
1766     my $authorised_values = shift;
1767
1768     my @imagelist;
1769
1770     my $authorised_value_list = GetAuthorisedValues();
1771     # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1772     foreach my $this_authorised_value ( @$authorised_value_list ) {
1773         if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1774              && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1775             # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1776             if ( defined $this_authorised_value->{'imageurl'} ) {
1777                 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1778                                    label    => $this_authorised_value->{'lib'},
1779                                    category => $this_authorised_value->{'category'},
1780                                    value    => $this_authorised_value->{'authorised_value'}, };
1781             }
1782         }
1783     }
1784
1785     # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1786     return \@imagelist;
1787
1788 }
1789
1790 =head1 LIMITED USE FUNCTIONS
1791
1792 The following functions, while part of the public API,
1793 are not exported.  This is generally because they are
1794 meant to be used by only one script for a specific
1795 purpose, and should not be used in any other context
1796 without careful thought.
1797
1798 =cut
1799
1800 =head2 GetMarcItem
1801
1802   my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1803
1804 Returns MARC::Record of the item passed in parameter.
1805 This function is meant for use only in C<cataloguing/additem.pl>,
1806 where it is needed to support that script's MARC-like
1807 editor.
1808
1809 =cut
1810
1811 sub GetMarcItem {
1812     my ( $biblionumber, $itemnumber ) = @_;
1813
1814     # GetMarcItem has been revised so that it does the following:
1815     #  1. Gets the item information from the items table.
1816     #  2. Converts it to a MARC field for storage in the bib record.
1817     #
1818     # The previous behavior was:
1819     #  1. Get the bib record.
1820     #  2. Return the MARC tag corresponding to the item record.
1821     #
1822     # The difference is that one treats the items row as authoritative,
1823     # while the other treats the MARC representation as authoritative
1824     # under certain circumstances.
1825
1826     my $itemrecord = GetItem($itemnumber);
1827
1828     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1829     # Also, don't emit a subfield if the underlying field is blank.
1830
1831     
1832     return Item2Marc($itemrecord,$biblionumber);
1833
1834 }
1835 sub Item2Marc {
1836         my ($itemrecord,$biblionumber)=@_;
1837     my $mungeditem = { 
1838         map {  
1839             defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  
1840         } keys %{ $itemrecord } 
1841     };
1842     my $itemmarc = TransformKohaToMarc($mungeditem);
1843     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1844
1845     my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1846     if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1847                 foreach my $field ($itemmarc->field($itemtag)){
1848             $field->add_subfields(@$unlinked_item_subfields);
1849         }
1850     }
1851         return $itemmarc;
1852 }
1853
1854 =head1 PRIVATE FUNCTIONS AND VARIABLES
1855
1856 The following functions are not meant to be called
1857 directly, but are documented in order to explain
1858 the inner workings of C<C4::Items>.
1859
1860 =cut
1861
1862 =head2 %derived_columns
1863
1864 This hash keeps track of item columns that
1865 are strictly derived from other columns in
1866 the item record and are not meant to be set
1867 independently.
1868
1869 Each key in the hash should be the name of a
1870 column (as named by TransformMarcToKoha).  Each
1871 value should be hashref whose keys are the
1872 columns on which the derived column depends.  The
1873 hashref should also contain a 'BUILDER' key
1874 that is a reference to a sub that calculates
1875 the derived value.
1876
1877 =cut
1878
1879 my %derived_columns = (
1880     'items.cn_sort' => {
1881         'itemcallnumber' => 1,
1882         'items.cn_source' => 1,
1883         'BUILDER' => \&_calc_items_cn_sort,
1884     }
1885 );
1886
1887 =head2 _set_derived_columns_for_add 
1888
1889   _set_derived_column_for_add($item);
1890
1891 Given an item hash representing a new item to be added,
1892 calculate any derived columns.  Currently the only
1893 such column is C<items.cn_sort>.
1894
1895 =cut
1896
1897 sub _set_derived_columns_for_add {
1898     my $item = shift;
1899
1900     foreach my $column (keys %derived_columns) {
1901         my $builder = $derived_columns{$column}->{'BUILDER'};
1902         my $source_values = {};
1903         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1904             next if $source_column eq 'BUILDER';
1905             $source_values->{$source_column} = $item->{$source_column};
1906         }
1907         $builder->($item, $source_values);
1908     }
1909 }
1910
1911 =head2 _set_derived_columns_for_mod 
1912
1913   _set_derived_column_for_mod($item);
1914
1915 Given an item hash representing a new item to be modified.
1916 calculate any derived columns.  Currently the only
1917 such column is C<items.cn_sort>.
1918
1919 This routine differs from C<_set_derived_columns_for_add>
1920 in that it needs to handle partial item records.  In other
1921 words, the caller of C<ModItem> may have supplied only one
1922 or two columns to be changed, so this function needs to
1923 determine whether any of the columns to be changed affect
1924 any of the derived columns.  Also, if a derived column
1925 depends on more than one column, but the caller is not
1926 changing all of then, this routine retrieves the unchanged
1927 values from the database in order to ensure a correct
1928 calculation.
1929
1930 =cut
1931
1932 sub _set_derived_columns_for_mod {
1933     my $item = shift;
1934
1935     foreach my $column (keys %derived_columns) {
1936         my $builder = $derived_columns{$column}->{'BUILDER'};
1937         my $source_values = {};
1938         my %missing_sources = ();
1939         my $must_recalc = 0;
1940         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1941             next if $source_column eq 'BUILDER';
1942             if (exists $item->{$source_column}) {
1943                 $must_recalc = 1;
1944                 $source_values->{$source_column} = $item->{$source_column};
1945             } else {
1946                 $missing_sources{$source_column} = 1;
1947             }
1948         }
1949         if ($must_recalc) {
1950             foreach my $source_column (keys %missing_sources) {
1951                 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1952             }
1953             $builder->($item, $source_values);
1954         }
1955     }
1956 }
1957
1958 =head2 _do_column_fixes_for_mod
1959
1960   _do_column_fixes_for_mod($item);
1961
1962 Given an item hashref containing one or more
1963 columns to modify, fix up certain values.
1964 Specifically, set to 0 any passed value
1965 of C<notforloan>, C<damaged>, C<itemlost>, or
1966 C<wthdrawn> that is either undefined or
1967 contains the empty string.
1968
1969 =cut
1970
1971 sub _do_column_fixes_for_mod {
1972     my $item = shift;
1973
1974     if (exists $item->{'notforloan'} and
1975         (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1976         $item->{'notforloan'} = 0;
1977     }
1978     if (exists $item->{'damaged'} and
1979         (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1980         $item->{'damaged'} = 0;
1981     }
1982     if (exists $item->{'itemlost'} and
1983         (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1984         $item->{'itemlost'} = 0;
1985     }
1986     if (exists $item->{'wthdrawn'} and
1987         (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1988         $item->{'wthdrawn'} = 0;
1989     }
1990     if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1991         $item->{'permanent_location'} = $item->{'location'};
1992     }
1993     if (exists $item->{'timestamp'}) {
1994         delete $item->{'timestamp'};
1995     }
1996 }
1997
1998 =head2 _get_single_item_column
1999
2000   _get_single_item_column($column, $itemnumber);
2001
2002 Retrieves the value of a single column from an C<items>
2003 row specified by C<$itemnumber>.
2004
2005 =cut
2006
2007 sub _get_single_item_column {
2008     my $column = shift;
2009     my $itemnumber = shift;
2010     
2011     my $dbh = C4::Context->dbh;
2012     my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
2013     $sth->execute($itemnumber);
2014     my ($value) = $sth->fetchrow();
2015     return $value; 
2016 }
2017
2018 =head2 _calc_items_cn_sort
2019
2020   _calc_items_cn_sort($item, $source_values);
2021
2022 Helper routine to calculate C<items.cn_sort>.
2023
2024 =cut
2025
2026 sub _calc_items_cn_sort {
2027     my $item = shift;
2028     my $source_values = shift;
2029
2030     $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2031 }
2032
2033 =head2 _set_defaults_for_add 
2034
2035   _set_defaults_for_add($item_hash);
2036
2037 Given an item hash representing an item to be added, set
2038 correct default values for columns whose default value
2039 is not handled by the DBMS.  This includes the following
2040 columns:
2041
2042 =over 2
2043
2044 =item * 
2045
2046 C<items.dateaccessioned>
2047
2048 =item *
2049
2050 C<items.notforloan>
2051
2052 =item *
2053
2054 C<items.damaged>
2055
2056 =item *
2057
2058 C<items.itemlost>
2059
2060 =item *
2061
2062 C<items.wthdrawn>
2063
2064 =back
2065
2066 =cut
2067
2068 sub _set_defaults_for_add {
2069     my $item = shift;
2070     $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
2071     $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
2072 }
2073
2074 =head2 _koha_new_item
2075
2076   my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
2077
2078 Perform the actual insert into the C<items> table.
2079
2080 =cut
2081
2082 sub _koha_new_item {
2083     my ( $item, $barcode ) = @_;
2084     my $dbh=C4::Context->dbh;  
2085     my $error;
2086     my $query =
2087            "INSERT INTO items SET
2088             biblionumber        = ?,
2089             biblioitemnumber    = ?,
2090             barcode             = ?,
2091             dateaccessioned     = ?,
2092             booksellerid        = ?,
2093             homebranch          = ?,
2094             price               = ?,
2095             replacementprice    = ?,
2096             replacementpricedate = ?,
2097             datelastborrowed    = ?,
2098             datelastseen        = ?,
2099             stack               = ?,
2100             notforloan          = ?,
2101             damaged             = ?,
2102             itemlost            = ?,
2103             wthdrawn            = ?,
2104             itemcallnumber      = ?,
2105             restricted          = ?,
2106             itemnotes           = ?,
2107             holdingbranch       = ?,
2108             paidfor             = ?,
2109             location            = ?,
2110             permanent_location            = ?,
2111             onloan              = ?,
2112             issues              = ?,
2113             renewals            = ?,
2114             reserves            = ?,
2115             cn_source           = ?,
2116             cn_sort             = ?,
2117             ccode               = ?,
2118             itype               = ?,
2119             materials           = ?,
2120             uri = ?,
2121             enumchron           = ?,
2122             more_subfields_xml  = ?,
2123             copynumber          = ?,
2124             stocknumber         = ?
2125           ";
2126     my $sth = $dbh->prepare($query);
2127     my $today = C4::Dates->today('iso');
2128    $sth->execute(
2129             $item->{'biblionumber'},
2130             $item->{'biblioitemnumber'},
2131             $barcode,
2132             $item->{'dateaccessioned'},
2133             $item->{'booksellerid'},
2134             $item->{'homebranch'},
2135             $item->{'price'},
2136             $item->{'replacementprice'},
2137             $item->{'replacementpricedate'} || $today,
2138             $item->{datelastborrowed},
2139             $item->{datelastseen} || $today,
2140             $item->{stack},
2141             $item->{'notforloan'},
2142             $item->{'damaged'},
2143             $item->{'itemlost'},
2144             $item->{'wthdrawn'},
2145             $item->{'itemcallnumber'},
2146             $item->{'restricted'},
2147             $item->{'itemnotes'},
2148             $item->{'holdingbranch'},
2149             $item->{'paidfor'},
2150             $item->{'location'},
2151             $item->{'permanent_location'},
2152             $item->{'onloan'},
2153             $item->{'issues'},
2154             $item->{'renewals'},
2155             $item->{'reserves'},
2156             $item->{'items.cn_source'},
2157             $item->{'items.cn_sort'},
2158             $item->{'ccode'},
2159             $item->{'itype'},
2160             $item->{'materials'},
2161             $item->{'uri'},
2162             $item->{'enumchron'},
2163             $item->{'more_subfields_xml'},
2164             $item->{'copynumber'},
2165             $item->{'stocknumber'},
2166     );
2167
2168     my $itemnumber;
2169     if ( defined $sth->errstr ) {
2170         $error.="ERROR in _koha_new_item $query".$sth->errstr;
2171     }
2172     else {
2173         $itemnumber = $dbh->{'mysql_insertid'};
2174     }
2175
2176     return ( $itemnumber, $error );
2177 }
2178
2179 =head2 MoveItemFromBiblio
2180
2181   MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2182
2183 Moves an item from a biblio to another
2184
2185 Returns undef if the move failed or the biblionumber of the destination record otherwise
2186
2187 =cut
2188
2189 sub MoveItemFromBiblio {
2190     my ($itemnumber, $frombiblio, $tobiblio) = @_;
2191     my $dbh = C4::Context->dbh;
2192     my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2193     $sth->execute( $tobiblio );
2194     my ( $tobiblioitem ) = $sth->fetchrow();
2195     $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2196     my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2197     if ($return == 1) {
2198         ModZebra( $tobiblio, "specialUpdate", "biblioserver", undef, undef );
2199         ModZebra( $frombiblio, "specialUpdate", "biblioserver", undef, undef );
2200             # Checking if the item we want to move is in an order 
2201         require C4::Acquisition;
2202         my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
2203             if ($order) {
2204                     # Replacing the biblionumber within the order if necessary
2205                     $order->{'biblionumber'} = $tobiblio;
2206                 C4::Acquisition::ModOrder($order);
2207             }
2208         return $tobiblio;
2209         }
2210     return;
2211 }
2212
2213 =head2 DelItemCheck
2214
2215    DelItemCheck($dbh, $biblionumber, $itemnumber);
2216
2217 Exported function (core API) for deleting an item record in Koha if there no current issue.
2218
2219 =cut
2220
2221 sub DelItemCheck {
2222     my ( $dbh, $biblionumber, $itemnumber ) = @_;
2223     my $error;
2224
2225         my $countanalytics=GetAnalyticsCount($itemnumber);
2226
2227
2228     # check that there is no issue on this item before deletion.
2229     my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2230     $sth->execute($itemnumber);
2231
2232     my $item = GetItem($itemnumber);
2233     my $onloan=$sth->fetchrow;
2234
2235     if ($onloan){
2236         $error = "book_on_loan" 
2237     }
2238     elsif ( !(C4::Context->userenv->{flags} & 1) and
2239             C4::Context->preference("IndependantBranches") and
2240            (C4::Context->userenv->{branch} ne
2241              $item->{C4::Context->preference("HomeOrHoldingBranch")||'homebranch'}) )
2242     {
2243         $error = "not_same_branch";
2244     }
2245         else{
2246         # check it doesnt have a waiting reserve
2247         $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2248         $sth->execute($itemnumber);
2249         my $reserve=$sth->fetchrow;
2250         if ($reserve){
2251             $error = "book_reserved";
2252         } elsif ($countanalytics > 0){
2253                 $error = "linked_analytics";
2254         } else {
2255             DelItem($dbh, $biblionumber, $itemnumber);
2256             return 1;
2257         }
2258     }
2259     return $error;
2260 }
2261
2262 =head2 _koha_modify_item
2263
2264   my ($itemnumber,$error) =_koha_modify_item( $item );
2265
2266 Perform the actual update of the C<items> row.  Note that this
2267 routine accepts a hashref specifying the columns to update.
2268
2269 =cut
2270
2271 sub _koha_modify_item {
2272     my ( $item ) = @_;
2273     my $dbh=C4::Context->dbh;  
2274     my $error;
2275
2276     my $query = "UPDATE items SET ";
2277     my @bind;
2278     for my $key ( keys %$item ) {
2279         $query.="$key=?,";
2280         push @bind, $item->{$key};
2281     }
2282     $query =~ s/,$//;
2283     $query .= " WHERE itemnumber=?";
2284     push @bind, $item->{'itemnumber'};
2285     my $sth = C4::Context->dbh->prepare($query);
2286     $sth->execute(@bind);
2287     if ( C4::Context->dbh->errstr ) {
2288         $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2289         warn $error;
2290     }
2291     return ($item->{'itemnumber'},$error);
2292 }
2293
2294 =head2 _koha_delete_item
2295
2296   _koha_delete_item( $dbh, $itemnum );
2297
2298 Internal function to delete an item record from the koha tables
2299
2300 =cut
2301
2302 sub _koha_delete_item {
2303     my ( $dbh, $itemnum ) = @_;
2304
2305     # save the deleted item to deleteditems table
2306     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2307     $sth->execute($itemnum);
2308     my $data = $sth->fetchrow_hashref();
2309     my $query = "INSERT INTO deleteditems SET ";
2310     my @bind  = ();
2311     foreach my $key ( keys %$data ) {
2312         $query .= "$key = ?,";
2313         push( @bind, $data->{$key} );
2314     }
2315     $query =~ s/\,$//;
2316     $sth = $dbh->prepare($query);
2317     $sth->execute(@bind);
2318
2319     # delete from items table
2320     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2321     $sth->execute($itemnum);
2322     return undef;
2323 }
2324
2325 =head2 _marc_from_item_hash
2326
2327   my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2328
2329 Given an item hash representing a complete item record,
2330 create a C<MARC::Record> object containing an embedded
2331 tag representing that item.
2332
2333 The third, optional parameter C<$unlinked_item_subfields> is
2334 an arrayref of subfields (not mapped to C<items> fields per the
2335 framework) to be added to the MARC representation
2336 of the item.
2337
2338 =cut
2339
2340 sub _marc_from_item_hash {
2341     my $item = shift;
2342     my $frameworkcode = shift;
2343     my $unlinked_item_subfields;
2344     if (@_) {
2345         $unlinked_item_subfields = shift;
2346     }
2347    
2348     # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2349     # Also, don't emit a subfield if the underlying field is blank.
2350     my $mungeditem = { map {  (defined($item->{$_}) and $item->{$_} ne '') ? 
2351                                 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_})) 
2352                                 : ()  } keys %{ $item } }; 
2353
2354     my $item_marc = MARC::Record->new();
2355     foreach my $item_field ( keys %{$mungeditem} ) {
2356         my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2357         next unless defined $tag and defined $subfield;    # skip if not mapped to MARC field
2358         my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2359         foreach my $value (@values){
2360             if ( my $field = $item_marc->field($tag) ) {
2361                     $field->add_subfields( $subfield => $value );
2362             } else {
2363                 my $add_subfields = [];
2364                 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2365                     $add_subfields = $unlinked_item_subfields;
2366             }
2367             $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2368             }
2369         }
2370     }
2371
2372     return $item_marc;
2373 }
2374
2375 =head2 _repack_item_errors
2376
2377 Add an error message hash generated by C<CheckItemPreSave>
2378 to a list of errors.
2379
2380 =cut
2381
2382 sub _repack_item_errors {
2383     my $item_sequence_num = shift;
2384     my $item_ref = shift;
2385     my $error_ref = shift;
2386
2387     my @repacked_errors = ();
2388
2389     foreach my $error_code (sort keys %{ $error_ref }) {
2390         my $repacked_error = {};
2391         $repacked_error->{'item_sequence'} = $item_sequence_num;
2392         $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2393         $repacked_error->{'error_code'} = $error_code;
2394         $repacked_error->{'error_information'} = $error_ref->{$error_code};
2395         push @repacked_errors, $repacked_error;
2396     } 
2397
2398     return @repacked_errors;
2399 }
2400
2401 =head2 _get_unlinked_item_subfields
2402
2403   my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2404
2405 =cut
2406
2407 sub _get_unlinked_item_subfields {
2408     my $original_item_marc = shift;
2409     my $frameworkcode = shift;
2410
2411     my $marcstructure = GetMarcStructure(1, $frameworkcode);
2412
2413     # assume that this record has only one field, and that that
2414     # field contains only the item information
2415     my $subfields = [];
2416     my @fields = $original_item_marc->fields();
2417     if ($#fields > -1) {
2418         my $field = $fields[0];
2419             my $tag = $field->tag();
2420         foreach my $subfield ($field->subfields()) {
2421             if (defined $subfield->[1] and
2422                 $subfield->[1] ne '' and
2423                 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2424                 push @$subfields, $subfield->[0] => $subfield->[1];
2425             }
2426         }
2427     }
2428     return $subfields;
2429 }
2430
2431 =head2 _get_unlinked_subfields_xml
2432
2433   my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2434
2435 =cut
2436
2437 sub _get_unlinked_subfields_xml {
2438     my $unlinked_item_subfields = shift;
2439
2440     my $xml;
2441     if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2442         my $marc = MARC::Record->new();
2443         # use of tag 999 is arbitrary, and doesn't need to match the item tag
2444         # used in the framework
2445         $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2446         $marc->encoding("UTF-8");    
2447         $xml = $marc->as_xml("USMARC");
2448     }
2449
2450     return $xml;
2451 }
2452
2453 =head2 _parse_unlinked_item_subfields_from_xml
2454
2455   my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2456
2457 =cut
2458
2459 sub  _parse_unlinked_item_subfields_from_xml {
2460     my $xml = shift;
2461     require C4::Charset;
2462     return unless defined $xml and $xml ne "";
2463     my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8');
2464     my $unlinked_subfields = [];
2465     my @fields = $marc->fields();
2466     if ($#fields > -1) {
2467         foreach my $subfield ($fields[0]->subfields()) {
2468             push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2469         }
2470     }
2471     return $unlinked_subfields;
2472 }
2473
2474 =head2 GetAnalyticsCount
2475
2476   $count= &GetAnalyticsCount($itemnumber)
2477
2478 counts Usage of itemnumber in Analytical bibliorecords. 
2479
2480 =cut
2481
2482 sub GetAnalyticsCount {
2483     my ($itemnumber) = @_;
2484     if (C4::Context->preference('NoZebra')) {
2485         # Read the index Koha-Auth-Number for this authid and count the lines
2486         my $result = C4::Search::NZanalyse("hi=$itemnumber");
2487         my @tab = split /;/,$result;
2488         return scalar @tab;
2489     } else {
2490         ### ZOOM search here
2491         my $query;
2492         $query= "hi=".$itemnumber;
2493                 my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
2494         return ($result);
2495     }
2496 }
2497
2498 =head2 GetItemHolds
2499
2500 =over 4
2501 $holds = &GetItemHolds($biblionumber, $itemnumber);
2502
2503 =back
2504
2505 This function return the count of holds with $biblionumber and $itemnumber
2506
2507 =cut
2508
2509 sub GetItemHolds {
2510     my ($biblionumber, $itemnumber) = @_;
2511     my $holds;
2512     my $dbh            = C4::Context->dbh;
2513     my $query          = "SELECT count(*)
2514         FROM  reserves
2515         WHERE biblionumber=? AND itemnumber=?";
2516     my $sth = $dbh->prepare($query);
2517     $sth->execute($biblionumber, $itemnumber);
2518     $holds = $sth->fetchrow;
2519     return $holds;
2520 }
2521
2522 # Return the list of the column names of items table
2523 sub _get_items_columns {
2524     my $dbh = C4::Context->dbh;
2525     my $sth = $dbh->column_info(undef, undef, 'items', '%');
2526     $sth->execute;
2527     my $results = $sth->fetchall_hashref('COLUMN_NAME');
2528     return keys %$results;
2529 }
2530
2531 =head2 SearchItems
2532
2533     my $items = SearchItems($field, $value);
2534
2535 SearchItems will search for items on a specific given field.
2536 For instance you can search all items with a specific stocknumber like this:
2537
2538     my $items = SearchItems('stocknumber', $stocknumber);
2539
2540 =cut
2541
2542 sub SearchItems {
2543     my ($field, $value) = @_;
2544
2545     my $dbh = C4::Context->dbh;
2546     my @columns = _get_items_columns;
2547     my $results = [];
2548     if(0 < grep /^$field$/, @columns) {
2549         my $query = "SELECT $field FROM items WHERE $field = ?";
2550         my $sth = $dbh->prepare( $query );
2551         $sth->execute( $value );
2552         $results = $sth->fetchall_arrayref({});
2553     }
2554     return $results;
2555 }
2556
2557
2558 =head1  OTHER FUNCTIONS
2559
2560 =head2 _find_value
2561
2562   ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2563
2564 Find the given $subfield in the given $tag in the given
2565 MARC::Record $record.  If the subfield is found, returns
2566 the (indicators, value) pair; otherwise, (undef, undef) is
2567 returned.
2568
2569 PROPOSITION :
2570 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2571 I suggest we export it from this module.
2572
2573 =cut
2574
2575 sub _find_value {
2576     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2577     my @result;
2578     my $indicator;
2579     if ( $tagfield < 10 ) {
2580         if ( $record->field($tagfield) ) {
2581             push @result, $record->field($tagfield)->data();
2582         } else {
2583             push @result, "";
2584         }
2585     } else {
2586         foreach my $field ( $record->field($tagfield) ) {
2587             my @subfields = $field->subfields();
2588             foreach my $subfield (@subfields) {
2589                 if ( @$subfield[0] eq $insubfield ) {
2590                     push @result, @$subfield[1];
2591                     $indicator = $field->indicator(1) . $field->indicator(2);
2592                 }
2593             }
2594         }
2595     }
2596     return ( $indicator, @result );
2597 }
2598
2599
2600 =head2 PrepareItemrecordDisplay
2601
2602   PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2603
2604 Returns a hash with all the fields for Display a given item data in a template
2605
2606 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2607
2608 =cut
2609
2610 sub PrepareItemrecordDisplay {
2611
2612     my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2613
2614     my $dbh = C4::Context->dbh;
2615     $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2616     my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2617     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2618
2619     # return nothing if we don't have found an existing framework.
2620     return q{} unless $tagslib;
2621     my $itemrecord;
2622     if ($itemnum) {
2623         $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
2624     }
2625     my @loop_data;
2626     my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" );
2627     foreach my $tag ( sort keys %{$tagslib} ) {
2628         my $previous_tag = '';
2629         if ( $tag ne '' ) {
2630
2631             # loop through each subfield
2632             my $cntsubf;
2633             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2634                 next if ( subfield_is_koha_internal_p($subfield) );
2635                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2636                 my %subfield_data;
2637                 $subfield_data{tag}           = $tag;
2638                 $subfield_data{subfield}      = $subfield;
2639                 $subfield_data{countsubfield} = $cntsubf++;
2640                 $subfield_data{kohafield}     = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2641                 $subfield_data{id}            = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2642
2643                 #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2644                 $subfield_data{marc_lib}   = $tagslib->{$tag}->{$subfield}->{lib};
2645                 $subfield_data{mandatory}  = $tagslib->{$tag}->{$subfield}->{mandatory};
2646                 $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2647                 $subfield_data{hidden}     = "display:none"
2648                   if $tagslib->{$tag}->{$subfield}->{hidden};
2649                 my ( $x, $defaultvalue );
2650                 if ($itemrecord) {
2651                     ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2652                 }
2653                 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2654                 if ( !defined $defaultvalue ) {
2655                     $defaultvalue = q||;
2656                 }
2657                 $defaultvalue =~ s/"/&quot;/g;
2658
2659                 # search for itemcallnumber if applicable
2660                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2661                     && C4::Context->preference('itemcallnumber') ) {
2662                     my $CNtag      = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2663                     my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2664                     if ($itemrecord) {
2665                         my $temp = $itemrecord->field($CNtag);
2666                         if ($temp) {
2667                             $defaultvalue = $temp->subfield($CNsubfield);
2668                         }
2669                     }
2670                 }
2671                 if (   $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2672                     && $defaultvalues
2673                     && $defaultvalues->{'callnumber'} ) {
2674                     my $temp;
2675                     if ($itemrecord) {
2676                         $temp = $itemrecord->field($subfield);
2677                     }
2678                     unless ($temp) {
2679                         $defaultvalue = $defaultvalues->{'callnumber'} if $defaultvalues;
2680                     }
2681                 }
2682                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2683                     && $defaultvalues
2684                     && $defaultvalues->{'branchcode'} ) {
2685                     my $temp;
2686                     if ($itemrecord) {
2687                         $temp = $itemrecord->field($subfield);
2688                     }
2689                     unless ($temp) {
2690                         $defaultvalue = $defaultvalues->{branchcode} if $defaultvalues;
2691                     }
2692                 }
2693                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2694                     && $defaultvalues
2695                     && $defaultvalues->{'location'} ) {
2696                     my $temp = $itemrecord->field($subfield) if ($itemrecord);
2697                     unless ($temp) {
2698                         $defaultvalue = $defaultvalues->{location} if $defaultvalues;
2699                     }
2700                 }
2701                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2702                     my @authorised_values;
2703                     my %authorised_lib;
2704
2705                     # builds list, depending on authorised value...
2706                     #---- branch
2707                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2708                         if (   ( C4::Context->preference("IndependantBranches") )
2709                             && ( C4::Context->userenv->{flags} % 2 != 1 ) ) {
2710                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2711                             $sth->execute( C4::Context->userenv->{branch} );
2712                             push @authorised_values, ""
2713                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2714                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2715                                 push @authorised_values, $branchcode;
2716                                 $authorised_lib{$branchcode} = $branchname;
2717                             }
2718                         } else {
2719                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2720                             $sth->execute;
2721                             push @authorised_values, ""
2722                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2723                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2724                                 push @authorised_values, $branchcode;
2725                                 $authorised_lib{$branchcode} = $branchname;
2726                             }
2727                         }
2728
2729                         #----- itemtypes
2730                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2731                         my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2732                         $sth->execute;
2733                         push @authorised_values, ""
2734                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2735                         while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2736                             push @authorised_values, $itemtype;
2737                             $authorised_lib{$itemtype} = $description;
2738                         }
2739                         #---- class_sources
2740                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2741                         push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2742
2743                         my $class_sources = GetClassSources();
2744                         my $default_source = C4::Context->preference("DefaultClassificationSource");
2745
2746                         foreach my $class_source (sort keys %$class_sources) {
2747                             next unless $class_sources->{$class_source}->{'used'} or
2748                                         ($class_source eq $default_source);
2749                             push @authorised_values, $class_source;
2750                             $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2751                         }
2752
2753                         #---- "true" authorised value
2754                     } else {
2755                         $authorised_values_sth->execute( $tagslib->{$tag}->{$subfield}->{authorised_value} );
2756                         push @authorised_values, ""
2757                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2758                         while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2759                             push @authorised_values, $value;
2760                             $authorised_lib{$value} = $lib;
2761                         }
2762                     }
2763                     $subfield_data{marc_value} = CGI::scrolling_list(
2764                         -name     => 'field_value',
2765                         -values   => \@authorised_values,
2766                         -default  => "$defaultvalue",
2767                         -labels   => \%authorised_lib,
2768                         -size     => 1,
2769                         -tabindex => '',
2770                         -multiple => 0,
2771                     );
2772                 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2773                         # opening plugin
2774                         my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2775                         if (do $plugin) {
2776                             my $temp;
2777                             my $extended_param = plugin_parameters( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2778                             my ( $function_name, $javascript ) = plugin_javascript( $dbh, $temp, $tagslib, $subfield_data{id}, undef );
2779                             $subfield_data{random}     = int(rand(1000000));    # why do we need 2 different randoms?
2780                             $subfield_data{marc_value} = qq[<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255"
2781                                 onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2782                                  onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2783                                 <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2784                                 $javascript];
2785                         } else {
2786                             warn "Plugin Failed: $plugin";
2787                             $subfield_data{marc_value} = qq(<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" />); # supply default input form
2788                         }
2789                 }
2790                 elsif ( $tag eq '' ) {       # it's an hidden field
2791                     $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2792                 }
2793                 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) {   # FIXME: shouldn't input type be "hidden" ?
2794                     $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2795                 }
2796                 elsif ( length($defaultvalue) > 100
2797                             or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2798                                   300 <= $tag && $tag < 400 && $subfield eq 'a' )
2799                             or (C4::Context->preference("marcflavour") eq "MARC21"  and
2800                                   500 <= $tag && $tag < 600                     )
2801                           ) {
2802                     # oversize field (textarea)
2803                     $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255">$defaultvalue</textarea>\n");
2804                 } else {
2805                     $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2806                 }
2807                 push( @loop_data, \%subfield_data );
2808             }
2809         }
2810     }
2811     my $itemnumber;
2812     if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
2813         $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
2814     }
2815     return {
2816         'itemtagfield'    => $itemtagfield,
2817         'itemtagsubfield' => $itemtagsubfield,
2818         'itemnumber'      => $itemnumber,
2819         'iteminformation' => \@loop_data
2820     };
2821 }
2822
2823 1;