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