Bug 7684: multiple fixes for inventory
[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, 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     # backup the record
626     my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
627     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
628     # This last update statement makes that the timestamp column in deleteditems is updated too. If you remove these lines, please add a line to update the timestamp separately. See Bugzilla report 7146 and Biblio.pm (DelBiblio).
629
630     #search item field code
631     logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
632 }
633
634 =head2 CheckItemPreSave
635
636     my $item_ref = TransformMarcToKoha($marc, 'items');
637     # do stuff
638     my %errors = CheckItemPreSave($item_ref);
639     if (exists $errors{'duplicate_barcode'}) {
640         print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
641     } elsif (exists $errors{'invalid_homebranch'}) {
642         print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
643     } elsif (exists $errors{'invalid_holdingbranch'}) {
644         print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
645     } else {
646         print "item is OK";
647     }
648
649 Given a hashref containing item fields, determine if it can be
650 inserted or updated in the database.  Specifically, checks for
651 database integrity issues, and returns a hash containing any
652 of the following keys, if applicable.
653
654 =over 2
655
656 =item duplicate_barcode
657
658 Barcode, if it duplicates one already found in the database.
659
660 =item invalid_homebranch
661
662 Home branch, if not defined in branches table.
663
664 =item invalid_holdingbranch
665
666 Holding branch, if not defined in branches table.
667
668 =back
669
670 This function does NOT implement any policy-related checks,
671 e.g., whether current operator is allowed to save an
672 item that has a given branch code.
673
674 =cut
675
676 sub CheckItemPreSave {
677     my $item_ref = shift;
678     require C4::Branch;
679
680     my %errors = ();
681
682     # check for duplicate barcode
683     if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
684         my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
685         if ($existing_itemnumber) {
686             if (!exists $item_ref->{'itemnumber'}                       # new item
687                 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
688                 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
689             }
690         }
691     }
692
693     # check for valid home branch
694     if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
695         my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'});
696         unless (defined $branch_name) {
697             # relies on fact that branches.branchname is a non-NULL column,
698             # so GetBranchName returns undef only if branch does not exist
699             $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
700         }
701     }
702
703     # check for valid holding branch
704     if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
705         my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'});
706         unless (defined $branch_name) {
707             # relies on fact that branches.branchname is a non-NULL column,
708             # so GetBranchName returns undef only if branch does not exist
709             $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
710         }
711     }
712
713     return %errors;
714
715 }
716
717 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
718
719 The following functions provide various ways of 
720 getting an item record, a set of item records, or
721 lists of authorized values for certain item fields.
722
723 Some of the functions in this group are candidates
724 for refactoring -- for example, some of the code
725 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
726 has copy-and-paste work.
727
728 =cut
729
730 =head2 GetItemStatus
731
732   $itemstatushash = GetItemStatus($fwkcode);
733
734 Returns a list of valid values for the
735 C<items.notforloan> field.
736
737 NOTE: does B<not> return an individual item's
738 status.
739
740 Can be MARC dependant.
741 fwkcode is optional.
742 But basically could be can be loan or not
743 Create a status selector with the following code
744
745 =head3 in PERL SCRIPT
746
747  my $itemstatushash = getitemstatus;
748  my @itemstatusloop;
749  foreach my $thisstatus (keys %$itemstatushash) {
750      my %row =(value => $thisstatus,
751                  statusname => $itemstatushash->{$thisstatus}->{'statusname'},
752              );
753      push @itemstatusloop, \%row;
754  }
755  $template->param(statusloop=>\@itemstatusloop);
756
757 =head3 in TEMPLATE
758
759 <select name="statusloop" id="statusloop">
760     <option value="">Default</option>
761     [% FOREACH statusloo IN statusloop %]
762         [% IF ( statusloo.selected ) %]
763             <option value="[% statusloo.value %]" selected="selected">[% statusloo.statusname %]</option>
764         [% ELSE %]
765             <option value="[% statusloo.value %]">[% statusloo.statusname %]</option>
766         [% END %]
767     [% END %]
768 </select>
769
770 =cut
771
772 sub GetItemStatus {
773
774     # returns a reference to a hash of references to status...
775     my ($fwk) = @_;
776     my %itemstatus;
777     my $dbh = C4::Context->dbh;
778     my $sth;
779     $fwk = '' unless ($fwk);
780     my ( $tag, $subfield ) =
781       GetMarcFromKohaField( "items.notforloan", $fwk );
782     if ( $tag and $subfield ) {
783         my $sth =
784           $dbh->prepare(
785             "SELECT authorised_value
786             FROM marc_subfield_structure
787             WHERE tagfield=?
788                 AND tagsubfield=?
789                 AND frameworkcode=?
790             "
791           );
792         $sth->execute( $tag, $subfield, $fwk );
793         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
794             my $authvalsth =
795               $dbh->prepare(
796                 "SELECT authorised_value,lib
797                 FROM authorised_values 
798                 WHERE category=? 
799                 ORDER BY lib
800                 "
801               );
802             $authvalsth->execute($authorisedvaluecat);
803             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
804                 $itemstatus{$authorisedvalue} = $lib;
805             }
806             return \%itemstatus;
807             exit 1;
808         }
809         else {
810
811             #No authvalue list
812             # build default
813         }
814     }
815
816     #No authvalue list
817     #build default
818     $itemstatus{"1"} = "Not For Loan";
819     return \%itemstatus;
820 }
821
822 =head2 GetItemLocation
823
824   $itemlochash = GetItemLocation($fwk);
825
826 Returns a list of valid values for the
827 C<items.location> field.
828
829 NOTE: does B<not> return an individual item's
830 location.
831
832 where fwk stands for an optional framework code.
833 Create a location selector with the following code
834
835 =head3 in PERL SCRIPT
836
837   my $itemlochash = getitemlocation;
838   my @itemlocloop;
839   foreach my $thisloc (keys %$itemlochash) {
840       my $selected = 1 if $thisbranch eq $branch;
841       my %row =(locval => $thisloc,
842                   selected => $selected,
843                   locname => $itemlochash->{$thisloc},
844                );
845       push @itemlocloop, \%row;
846   }
847   $template->param(itemlocationloop => \@itemlocloop);
848
849 =head3 in TEMPLATE
850
851   <select name="location">
852       <option value="">Default</option>
853   <!-- TMPL_LOOP name="itemlocationloop" -->
854       <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
855   <!-- /TMPL_LOOP -->
856   </select>
857
858 =cut
859
860 sub GetItemLocation {
861
862     # returns a reference to a hash of references to location...
863     my ($fwk) = @_;
864     my %itemlocation;
865     my $dbh = C4::Context->dbh;
866     my $sth;
867     $fwk = '' unless ($fwk);
868     my ( $tag, $subfield ) =
869       GetMarcFromKohaField( "items.location", $fwk );
870     if ( $tag and $subfield ) {
871         my $sth =
872           $dbh->prepare(
873             "SELECT authorised_value
874             FROM marc_subfield_structure 
875             WHERE tagfield=? 
876                 AND tagsubfield=? 
877                 AND frameworkcode=?"
878           );
879         $sth->execute( $tag, $subfield, $fwk );
880         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
881             my $authvalsth =
882               $dbh->prepare(
883                 "SELECT authorised_value,lib
884                 FROM authorised_values
885                 WHERE category=?
886                 ORDER BY lib"
887               );
888             $authvalsth->execute($authorisedvaluecat);
889             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
890                 $itemlocation{$authorisedvalue} = $lib;
891             }
892             return \%itemlocation;
893             exit 1;
894         }
895         else {
896
897             #No authvalue list
898             # build default
899         }
900     }
901
902     #No authvalue list
903     #build default
904     $itemlocation{"1"} = "Not For Loan";
905     return \%itemlocation;
906 }
907
908 =head2 GetLostItems
909
910   $items = GetLostItems( $where, $orderby );
911
912 This function gets a list of lost items.
913
914 =over 2
915
916 =item input:
917
918 C<$where> is a hashref. it containts a field of the items table as key
919 and the value to match as value. For example:
920
921 { barcode    => 'abc123',
922   homebranch => 'CPL',    }
923
924 C<$orderby> is a field of the items table by which the resultset
925 should be orderd.
926
927 =item return:
928
929 C<$items> is a reference to an array full of hashrefs with columns
930 from the "items" table as keys.
931
932 =item usage in the perl script:
933
934   my $where = { barcode => '0001548' };
935   my $items = GetLostItems( $where, "homebranch" );
936   $template->param( itemsloop => $items );
937
938 =back
939
940 =cut
941
942 sub GetLostItems {
943     # Getting input args.
944     my $where   = shift;
945     my $orderby = shift;
946     my $dbh     = C4::Context->dbh;
947
948     my $query   = "
949         SELECT title, author, lib, itemlost, authorised_value, barcode, datelastseen, price, replacementprice, homebranch,
950                itype, itemtype, holdingbranch, location, itemnotes, items.biblionumber as biblionumber
951         FROM   items
952             LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
953             LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
954             LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
955         WHERE
956                 authorised_values.category = 'LOST'
957                 AND itemlost IS NOT NULL
958                 AND itemlost <> 0
959     ";
960     my @query_parameters;
961     foreach my $key (keys %$where) {
962         $query .= " AND $key LIKE ?";
963         push @query_parameters, "%$where->{$key}%";
964     }
965     my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
966     
967     if ( defined $orderby && grep($orderby, @ordervalues)) {
968         $query .= ' ORDER BY '.$orderby;
969     }
970
971     my $sth = $dbh->prepare($query);
972     $sth->execute( @query_parameters );
973     my $items = [];
974     while ( my $row = $sth->fetchrow_hashref ){
975         push @$items, $row;
976     }
977     return $items;
978 }
979
980 =head2 GetItemsForInventory
981
982 ($itemlist, $iTotalRecords)  = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $offset, $size, $statushash);
983
984 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
985
986 The sub returns a reference to a list of hashes, each containing
987 itemnumber, author, title, barcode, item callnumber, and date last
988 seen. It is ordered by callnumber then title.
989
990 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
991 the datelastseen can be used to specify that you want to see items not seen since a past date only.
992 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
993 $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.
994
995 $iTotalRecords is the number of rows that would have been returned without the $offset, $size limit clause
996
997 =cut
998
999 sub GetItemsForInventory {
1000     my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
1001     my $dbh = C4::Context->dbh;
1002     my ( @bind_params, @where_strings );
1003
1004     my $query = <<'END_SQL';
1005 SELECT SQL_CALC_FOUND_ROWS items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, biblio.frameworkcode, datelastseen, homebranch, location, notforloan, damaged, itemlost, stocknumber
1006 FROM items
1007   LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1008   LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1009 END_SQL
1010     if ($statushash){
1011         for my $authvfield (keys %$statushash){
1012             if ( scalar @{$statushash->{$authvfield}} > 0 ){
1013                 my $joinedvals = join ',', @{$statushash->{$authvfield}};
1014                 push @where_strings, "$authvfield in (" . $joinedvals . ")";
1015             }
1016         }
1017     }
1018
1019     if ($minlocation) {
1020         push @where_strings, 'itemcallnumber >= ?';
1021         push @bind_params, $minlocation;
1022     }
1023
1024     if ($maxlocation) {
1025         push @where_strings, 'itemcallnumber <= ?';
1026         push @bind_params, $maxlocation;
1027     }
1028
1029     if ($datelastseen) {
1030         $datelastseen = format_date_in_iso($datelastseen);  
1031         push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1032         push @bind_params, $datelastseen;
1033     }
1034
1035     if ( $location ) {
1036         push @where_strings, 'items.location = ?';
1037         push @bind_params, $location;
1038     }
1039
1040     if ( $branchcode ) {
1041         if($branch eq "homebranch"){
1042         push @where_strings, 'items.homebranch = ?';
1043         }else{
1044             push @where_strings, 'items.holdingbranch = ?';
1045         }
1046         push @bind_params, $branchcode;
1047     }
1048
1049     if ( $itemtype ) {
1050         push @where_strings, 'biblioitems.itemtype = ?';
1051         push @bind_params, $itemtype;
1052     }
1053
1054     if ( $ignoreissued) {
1055         $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1056         push @where_strings, 'issues.date_due IS NULL';
1057     }
1058
1059     if ( @where_strings ) {
1060         $query .= 'WHERE ';
1061         $query .= join ' AND ', @where_strings;
1062     }
1063     $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1064     $query .= " LIMIT $offset, $size" if ($offset and $size);
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("SELECT FOUND_ROWS()");
1071     $sth->execute();
1072     my ($iTotalRecords) = $sth->fetchrow_array();
1073
1074     foreach my $row (@$tmpresults) {
1075         $row->{datelastseen} = format_date( $row->{datelastseen} );
1076
1077         # Auth values
1078         foreach (keys %$row) {
1079             # If the koha field is mapped to a marc field
1080             my ($f, $sf) = GetMarcFromKohaField("items.$_", $row->{'frameworkcode'});
1081             if ($f and $sf) {
1082                 # We replace the code with it's description
1083                 my $authvals = C4::Koha::GetKohaAuthorisedValuesFromField($f, $sf, $row->{'frameworkcode'});
1084                 $row->{$_} = $authvals->{$row->{$_}} if defined $authvals->{$row->{$_}};
1085             }
1086         }
1087         push @results, $row;
1088     }
1089
1090     return (\@results, $iTotalRecords);
1091 }
1092
1093 =head2 GetItemsCount
1094
1095   $count = &GetItemsCount( $biblionumber);
1096
1097 This function return count of item with $biblionumber
1098
1099 =cut
1100
1101 sub GetItemsCount {
1102     my ( $biblionumber ) = @_;
1103     my $dbh = C4::Context->dbh;
1104     my $query = "SELECT count(*)
1105           FROM  items 
1106           WHERE biblionumber=?";
1107     my $sth = $dbh->prepare($query);
1108     $sth->execute($biblionumber);
1109     my $count = $sth->fetchrow;  
1110     return ($count);
1111 }
1112
1113 =head2 GetItemInfosOf
1114
1115   GetItemInfosOf(@itemnumbers);
1116
1117 =cut
1118
1119 sub GetItemInfosOf {
1120     my @itemnumbers = @_;
1121
1122     my $query = '
1123         SELECT *
1124         FROM items
1125         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1126     ';
1127     return get_infos_of( $query, 'itemnumber' );
1128 }
1129
1130 =head2 GetItemsByBiblioitemnumber
1131
1132   GetItemsByBiblioitemnumber($biblioitemnumber);
1133
1134 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1135 Called by C<C4::XISBN>
1136
1137 =cut
1138
1139 sub GetItemsByBiblioitemnumber {
1140     my ( $bibitem ) = @_;
1141     my $dbh = C4::Context->dbh;
1142     my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1143     # Get all items attached to a biblioitem
1144     my $i = 0;
1145     my @results; 
1146     $sth->execute($bibitem) || die $sth->errstr;
1147     while ( my $data = $sth->fetchrow_hashref ) {  
1148         # Foreach item, get circulation information
1149         my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1150                                    WHERE itemnumber = ?
1151                                    AND issues.borrowernumber = borrowers.borrowernumber"
1152         );
1153         $sth2->execute( $data->{'itemnumber'} );
1154         if ( my $data2 = $sth2->fetchrow_hashref ) {
1155             # if item is out, set the due date and who it is out too
1156             $data->{'date_due'}   = $data2->{'date_due'};
1157             $data->{'cardnumber'} = $data2->{'cardnumber'};
1158             $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
1159         }
1160         else {
1161             # set date_due to blank, so in the template we check itemlost, and withdrawn
1162             $data->{'date_due'} = '';                                                                                                         
1163         }    # else         
1164         # Find the last 3 people who borrowed this item.                  
1165         my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1166                       AND old_issues.borrowernumber = borrowers.borrowernumber
1167                       ORDER BY returndate desc,timestamp desc LIMIT 3";
1168         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1169         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1170         my $i2 = 0;
1171         while ( my $data2 = $sth2->fetchrow_hashref ) {
1172             $data->{"timestamp$i2"} = $data2->{'timestamp'};
1173             $data->{"card$i2"}      = $data2->{'cardnumber'};
1174             $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
1175             $i2++;
1176         }
1177         push(@results,$data);
1178     } 
1179     return (\@results); 
1180 }
1181
1182 =head2 GetItemsInfo
1183
1184   @results = GetItemsInfo($biblionumber);
1185
1186 Returns information about items with the given biblionumber.
1187
1188 C<GetItemsInfo> returns a list of references-to-hash. Each element
1189 contains a number of keys. Most of them are attributes from the
1190 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1191 Koha database. Other keys include:
1192
1193 =over 2
1194
1195 =item C<$data-E<gt>{branchname}>
1196
1197 The name (not the code) of the branch to which the book belongs.
1198
1199 =item C<$data-E<gt>{datelastseen}>
1200
1201 This is simply C<items.datelastseen>, except that while the date is
1202 stored in YYYY-MM-DD format in the database, here it is converted to
1203 DD/MM/YYYY format. A NULL date is returned as C<//>.
1204
1205 =item C<$data-E<gt>{datedue}>
1206
1207 =item C<$data-E<gt>{class}>
1208
1209 This is the concatenation of C<biblioitems.classification>, the book's
1210 Dewey code, and C<biblioitems.subclass>.
1211
1212 =item C<$data-E<gt>{ocount}>
1213
1214 I think this is the number of copies of the book available.
1215
1216 =item C<$data-E<gt>{order}>
1217
1218 If this is set, it is set to C<One Order>.
1219
1220 =back
1221
1222 =cut
1223
1224 sub GetItemsInfo {
1225     my ( $biblionumber ) = @_;
1226     my $dbh   = C4::Context->dbh;
1227     # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1228     my $query = "
1229     SELECT items.*,
1230            biblio.*,
1231            biblioitems.volume,
1232            biblioitems.number,
1233            biblioitems.itemtype,
1234            biblioitems.isbn,
1235            biblioitems.issn,
1236            biblioitems.publicationyear,
1237            biblioitems.publishercode,
1238            biblioitems.volumedate,
1239            biblioitems.volumedesc,
1240            biblioitems.lccn,
1241            biblioitems.url,
1242            items.notforloan as itemnotforloan,
1243            itemtypes.description,
1244            itemtypes.notforloan as notforloan_per_itemtype,
1245            holding.branchurl,
1246            holding.branchname,
1247            holding.opac_info as branch_opac_info
1248      FROM items
1249      LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1250      LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1251      LEFT JOIN biblio      ON      biblio.biblionumber     = items.biblionumber
1252      LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1253      LEFT JOIN itemtypes   ON   itemtypes.itemtype         = "
1254      . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1255     $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1256     my $sth = $dbh->prepare($query);
1257     $sth->execute($biblionumber);
1258     my $i = 0;
1259     my @results;
1260     my $serial;
1261
1262     my $isth    = $dbh->prepare(
1263         "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1264         FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1265         WHERE  itemnumber = ?"
1266        );
1267         my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); 
1268         while ( my $data = $sth->fetchrow_hashref ) {
1269         my $datedue = '';
1270         $isth->execute( $data->{'itemnumber'} );
1271         if ( my $idata = $isth->fetchrow_hashref ) {
1272             $data->{borrowernumber} = $idata->{borrowernumber};
1273             $data->{cardnumber}     = $idata->{cardnumber};
1274             $data->{surname}     = $idata->{surname};
1275             $data->{firstname}     = $idata->{firstname};
1276             $data->{lastreneweddate} = $idata->{lastreneweddate};
1277             $datedue                = $idata->{'date_due'};
1278         if (C4::Context->preference("IndependentBranches")){
1279         my $userenv = C4::Context->userenv;
1280         if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { 
1281             $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1282         }
1283         }
1284         }
1285                 if ( $data->{'serial'}) {       
1286                         $ssth->execute($data->{'itemnumber'}) ;
1287                         ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1288                         $serial = 1;
1289         }
1290         #get branch information.....
1291         my $bsth = $dbh->prepare(
1292             "SELECT * FROM branches WHERE branchcode = ?
1293         "
1294         );
1295         $bsth->execute( $data->{'holdingbranch'} );
1296         if ( my $bdata = $bsth->fetchrow_hashref ) {
1297             $data->{'branchname'} = $bdata->{'branchname'};
1298         }
1299         $data->{'datedue'}        = $datedue;
1300
1301         # get notforloan complete status if applicable
1302         if ( my $code = C4::Koha::GetAuthValCode( 'items.notforloan', $data->{frameworkcode} ) ) {
1303             $data->{notforloanvalue}     = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan} );
1304             $data->{notforloanvalueopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan}, 1 );
1305         }
1306
1307         # get restricted status and description if applicable
1308         if ( my $code = C4::Koha::GetAuthValCode( 'items.restricted', $data->{frameworkcode} ) ) {
1309             $data->{restrictedopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted}, 1 );
1310             $data->{restricted}     = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted} );
1311         }
1312
1313         # my stack procedures
1314         if ( my $code = C4::Koha::GetAuthValCode( 'items.stack', $data->{frameworkcode} ) ) {
1315             $data->{stack}          = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{stack} );
1316         }
1317         # Find the last 3 people who borrowed this item.
1318         my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1319                                     WHERE itemnumber = ?
1320                                     AND old_issues.borrowernumber = borrowers.borrowernumber
1321                                     ORDER BY returndate DESC
1322                                     LIMIT 3");
1323         $sth2->execute($data->{'itemnumber'});
1324         my $ii = 0;
1325         while (my $data2 = $sth2->fetchrow_hashref()) {
1326             $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1327             $data->{"card$ii"}      = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1328             $data->{"borrower$ii"}  = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1329             $ii++;
1330         }
1331
1332         $results[$i] = $data;
1333         $i++;
1334     }
1335         if($serial) {
1336                 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1337         } else {
1338         return (@results);
1339         }
1340 }
1341
1342 =head2 GetItemsLocationInfo
1343
1344   my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1345
1346 Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1347
1348 C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1349
1350 =over 2
1351
1352 =item C<$data-E<gt>{homebranch}>
1353
1354 Branch Name of the item's homebranch
1355
1356 =item C<$data-E<gt>{holdingbranch}>
1357
1358 Branch Name of the item's holdingbranch
1359
1360 =item C<$data-E<gt>{location}>
1361
1362 Item's shelving location code
1363
1364 =item C<$data-E<gt>{location_intranet}>
1365
1366 The intranet description for the Shelving Location as set in authorised_values 'LOC'
1367
1368 =item C<$data-E<gt>{location_opac}>
1369
1370 The OPAC description for the Shelving Location as set in authorised_values 'LOC'.  Falls back to intranet description if no OPAC 
1371 description is set.
1372
1373 =item C<$data-E<gt>{itemcallnumber}>
1374
1375 Item's itemcallnumber
1376
1377 =item C<$data-E<gt>{cn_sort}>
1378
1379 Item's call number normalized for sorting
1380
1381 =back
1382   
1383 =cut
1384
1385 sub GetItemsLocationInfo {
1386         my $biblionumber = shift;
1387         my @results;
1388
1389         my $dbh = C4::Context->dbh;
1390         my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch, 
1391                             location, itemcallnumber, cn_sort
1392                      FROM items, branches as a, branches as b
1393                      WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode 
1394                      AND biblionumber = ?
1395                      ORDER BY cn_sort ASC";
1396         my $sth = $dbh->prepare($query);
1397         $sth->execute($biblionumber);
1398
1399         while ( my $data = $sth->fetchrow_hashref ) {
1400              $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1401              $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1402              push @results, $data;
1403         }
1404         return @results;
1405 }
1406
1407 =head2 GetHostItemsInfo
1408
1409         $hostiteminfo = GetHostItemsInfo($hostfield);
1410         Returns the iteminfo for items linked to records via a host field
1411
1412 =cut
1413
1414 sub GetHostItemsInfo {
1415         my ($record) = @_;
1416         my @returnitemsInfo;
1417
1418         if (C4::Context->preference('marcflavour') eq 'MARC21' ||
1419         C4::Context->preference('marcflavour') eq 'NORMARC'){
1420             foreach my $hostfield ( $record->field('773') ) {
1421                 my $hostbiblionumber = $hostfield->subfield("0");
1422                 my $linkeditemnumber = $hostfield->subfield("9");
1423                 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1424                 foreach my $hostitemInfo (@hostitemInfos){
1425                         if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1426                                 push (@returnitemsInfo,$hostitemInfo);
1427                                 last;
1428                         }
1429                 }
1430             }
1431         } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){
1432             foreach my $hostfield ( $record->field('461') ) {
1433                 my $hostbiblionumber = $hostfield->subfield("0");
1434                 my $linkeditemnumber = $hostfield->subfield("9");
1435                 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1436                 foreach my $hostitemInfo (@hostitemInfos){
1437                         if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1438                                 push (@returnitemsInfo,$hostitemInfo);
1439                                 last;
1440                         }
1441                 }
1442             }
1443         }
1444         return @returnitemsInfo;
1445 }
1446
1447
1448 =head2 GetLastAcquisitions
1449
1450   my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 
1451                                     'itemtypes' => ('BK','BD')}, 10);
1452
1453 =cut
1454
1455 sub  GetLastAcquisitions {
1456         my ($data,$max) = @_;
1457
1458         my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1459         
1460         my $number_of_branches = @{$data->{branches}};
1461         my $number_of_itemtypes   = @{$data->{itemtypes}};
1462         
1463         
1464         my @where = ('WHERE 1 '); 
1465         $number_of_branches and push @where
1466            , 'AND holdingbranch IN (' 
1467            , join(',', ('?') x $number_of_branches )
1468            , ')'
1469          ;
1470         
1471         $number_of_itemtypes and push @where
1472            , "AND $itemtype IN (" 
1473            , join(',', ('?') x $number_of_itemtypes )
1474            , ')'
1475          ;
1476
1477         my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1478                                  FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber) 
1479                                     RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1480                                     @where
1481                                     GROUP BY biblio.biblionumber 
1482                                     ORDER BY dateaccessioned DESC LIMIT $max";
1483
1484         my $dbh = C4::Context->dbh;
1485         my $sth = $dbh->prepare($query);
1486     
1487     $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1488         
1489         my @results;
1490         while( my $row = $sth->fetchrow_hashref){
1491                 push @results, {date => $row->{dateaccessioned} 
1492                                                 , biblionumber => $row->{biblionumber}
1493                                                 , title => $row->{title}};
1494         }
1495         
1496         return @results;
1497 }
1498
1499 =head2 GetItemnumbersForBiblio
1500
1501   my $itemnumbers = GetItemnumbersForBiblio($biblionumber);
1502
1503 Given a single biblionumber, return an arrayref of all the corresponding itemnumbers
1504
1505 =cut
1506
1507 sub GetItemnumbersForBiblio {
1508     my $biblionumber = shift;
1509     my @items;
1510     my $dbh = C4::Context->dbh;
1511     my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
1512     $sth->execute($biblionumber);
1513     while (my $result = $sth->fetchrow_hashref) {
1514         push @items, $result->{'itemnumber'};
1515     }
1516     return \@items;
1517 }
1518
1519 =head2 get_itemnumbers_of
1520
1521   my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1522
1523 Given a list of biblionumbers, return the list of corresponding itemnumbers
1524 for each biblionumber.
1525
1526 Return a reference on a hash where keys are biblionumbers and values are
1527 references on array of itemnumbers.
1528
1529 =cut
1530
1531 sub get_itemnumbers_of {
1532     my @biblionumbers = @_;
1533
1534     my $dbh = C4::Context->dbh;
1535
1536     my $query = '
1537         SELECT itemnumber,
1538             biblionumber
1539         FROM items
1540         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1541     ';
1542     my $sth = $dbh->prepare($query);
1543     $sth->execute(@biblionumbers);
1544
1545     my %itemnumbers_of;
1546
1547     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1548         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1549     }
1550
1551     return \%itemnumbers_of;
1552 }
1553
1554 =head2 get_hostitemnumbers_of
1555
1556   my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);
1557
1558 Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields
1559
1560 Return a reference on a hash where key is a biblionumber and values are
1561 references on array of itemnumbers.
1562
1563 =cut
1564
1565
1566 sub get_hostitemnumbers_of {
1567         my ($biblionumber) = @_;
1568         my $marcrecord = GetMarcBiblio($biblionumber);
1569         my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1570         
1571         my $marcflavor = C4::Context->preference('marcflavour');
1572         if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1573         $tag='773';
1574         $biblio_s='0';
1575         $item_s='9';
1576     } elsif ($marcflavor eq 'UNIMARC') {
1577         $tag='461';
1578         $biblio_s='0';
1579         $item_s='9';
1580     }
1581
1582     foreach my $hostfield ( $marcrecord->field($tag) ) {
1583         my $hostbiblionumber = $hostfield->subfield($biblio_s);
1584         my $linkeditemnumber = $hostfield->subfield($item_s);
1585         my @itemnumbers;
1586         if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber})
1587         {
1588             @itemnumbers = @$itemnumbers;
1589         }
1590         foreach my $itemnumber (@itemnumbers){
1591             if ($itemnumber eq $linkeditemnumber){
1592                 push (@returnhostitemnumbers,$itemnumber);
1593                 last;
1594             }
1595         }
1596     }
1597     return @returnhostitemnumbers;
1598 }
1599
1600
1601 =head2 GetItemnumberFromBarcode
1602
1603   $result = GetItemnumberFromBarcode($barcode);
1604
1605 =cut
1606
1607 sub GetItemnumberFromBarcode {
1608     my ($barcode) = @_;
1609     my $dbh = C4::Context->dbh;
1610
1611     my $rq =
1612       $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1613     $rq->execute($barcode);
1614     my ($result) = $rq->fetchrow;
1615     return ($result);
1616 }
1617
1618 =head2 GetBarcodeFromItemnumber
1619
1620   $result = GetBarcodeFromItemnumber($itemnumber);
1621
1622 =cut
1623
1624 sub GetBarcodeFromItemnumber {
1625     my ($itemnumber) = @_;
1626     my $dbh = C4::Context->dbh;
1627
1628     my $rq =
1629       $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1630     $rq->execute($itemnumber);
1631     my ($result) = $rq->fetchrow;
1632     return ($result);
1633 }
1634
1635 =head2 GetHiddenItemnumbers
1636
1637     my @itemnumbers_to_hide = GetHiddenItemnumbers(@items);
1638
1639 Given a list of items it checks which should be hidden from the OPAC given
1640 the current configuration. Returns a list of itemnumbers corresponding to
1641 those that should be hidden.
1642
1643 =cut
1644
1645 sub GetHiddenItemnumbers {
1646     my (@items) = @_;
1647     my @resultitems;
1648
1649     my $yaml = C4::Context->preference('OpacHiddenItems');
1650     return () if (! $yaml =~ /\S/ );
1651     $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1652     my $hidingrules;
1653     eval {
1654         $hidingrules = YAML::Load($yaml);
1655     };
1656     if ($@) {
1657         warn "Unable to parse OpacHiddenItems syspref : $@";
1658         return ();
1659     }
1660     my $dbh = C4::Context->dbh;
1661
1662     # For each item
1663     foreach my $item (@items) {
1664
1665         # We check each rule
1666         foreach my $field (keys %$hidingrules) {
1667             my $val;
1668             if (exists $item->{$field}) {
1669                 $val = $item->{$field};
1670             }
1671             else {
1672                 my $query = "SELECT $field from items where itemnumber = ?";
1673                 $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1674             }
1675             $val = '' unless defined $val;
1676
1677             # If the results matches the values in the yaml file
1678             if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1679
1680                 # We add the itemnumber to the list
1681                 push @resultitems, $item->{'itemnumber'};
1682
1683                 # If at least one rule matched for an item, no need to test the others
1684                 last;
1685             }
1686         }
1687     }
1688     return @resultitems;
1689 }
1690
1691 =head3 get_item_authorised_values
1692
1693 find the types and values for all authorised values assigned to this item.
1694
1695 parameters: itemnumber
1696
1697 returns: a hashref malling the authorised value to the value set for this itemnumber
1698
1699     $authorised_values = {
1700              'CCODE'      => undef,
1701              'DAMAGED'    => '0',
1702              'LOC'        => '3',
1703              'LOST'       => '0'
1704              'NOT_LOAN'   => '0',
1705              'RESTRICTED' => undef,
1706              'STACK'      => undef,
1707              'WITHDRAWN'  => '0',
1708              'branches'   => 'CPL',
1709              'cn_source'  => undef,
1710              'itemtypes'  => 'SER',
1711            };
1712
1713 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1714
1715 =cut
1716
1717 sub get_item_authorised_values {
1718     my $itemnumber = shift;
1719
1720     # assume that these entries in the authorised_value table are item level.
1721     my $query = q(SELECT distinct authorised_value, kohafield
1722                     FROM marc_subfield_structure
1723                     WHERE kohafield like 'item%'
1724                       AND authorised_value != '' );
1725
1726     my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1727     my $iteminfo = GetItem( $itemnumber );
1728     # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1729     my $return;
1730     foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1731         my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1732         $field =~ s/^items\.//;
1733         if ( exists $iteminfo->{ $field } ) {
1734             $return->{ $this_authorised_value } = $iteminfo->{ $field };
1735         }
1736     }
1737     # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1738     return $return;
1739 }
1740
1741 =head3 get_authorised_value_images
1742
1743 find a list of icons that are appropriate for display based on the
1744 authorised values for a biblio.
1745
1746 parameters: listref of authorised values, such as comes from
1747 get_item_authorised_values or
1748 from C4::Biblio::get_biblio_authorised_values
1749
1750 returns: listref of hashrefs for each image. Each hashref looks like this:
1751
1752       { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1753         label    => '',
1754         category => '',
1755         value    => '', }
1756
1757 Notes: Currently, I put on the full path to the images on the staff
1758 side. This should either be configurable or not done at all. Since I
1759 have to deal with 'intranet' or 'opac' in
1760 get_biblio_authorised_values, perhaps I should be passing it in.
1761
1762 =cut
1763
1764 sub get_authorised_value_images {
1765     my $authorised_values = shift;
1766
1767     my @imagelist;
1768
1769     my $authorised_value_list = GetAuthorisedValues();
1770     # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1771     foreach my $this_authorised_value ( @$authorised_value_list ) {
1772         if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1773              && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1774             # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1775             if ( defined $this_authorised_value->{'imageurl'} ) {
1776                 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1777                                    label    => $this_authorised_value->{'lib'},
1778                                    category => $this_authorised_value->{'category'},
1779                                    value    => $this_authorised_value->{'authorised_value'}, };
1780             }
1781         }
1782     }
1783
1784     # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1785     return \@imagelist;
1786
1787 }
1788
1789 =head1 LIMITED USE FUNCTIONS
1790
1791 The following functions, while part of the public API,
1792 are not exported.  This is generally because they are
1793 meant to be used by only one script for a specific
1794 purpose, and should not be used in any other context
1795 without careful thought.
1796
1797 =cut
1798
1799 =head2 GetMarcItem
1800
1801   my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1802
1803 Returns MARC::Record of the item passed in parameter.
1804 This function is meant for use only in C<cataloguing/additem.pl>,
1805 where it is needed to support that script's MARC-like
1806 editor.
1807
1808 =cut
1809
1810 sub GetMarcItem {
1811     my ( $biblionumber, $itemnumber ) = @_;
1812
1813     # GetMarcItem has been revised so that it does the following:
1814     #  1. Gets the item information from the items table.
1815     #  2. Converts it to a MARC field for storage in the bib record.
1816     #
1817     # The previous behavior was:
1818     #  1. Get the bib record.
1819     #  2. Return the MARC tag corresponding to the item record.
1820     #
1821     # The difference is that one treats the items row as authoritative,
1822     # while the other treats the MARC representation as authoritative
1823     # under certain circumstances.
1824
1825     my $itemrecord = GetItem($itemnumber);
1826
1827     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1828     # Also, don't emit a subfield if the underlying field is blank.
1829
1830     
1831     return Item2Marc($itemrecord,$biblionumber);
1832
1833 }
1834 sub Item2Marc {
1835         my ($itemrecord,$biblionumber)=@_;
1836     my $mungeditem = { 
1837         map {  
1838             defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  
1839         } keys %{ $itemrecord } 
1840     };
1841     my $itemmarc = TransformKohaToMarc($mungeditem);
1842     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1843
1844     my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1845     if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1846                 foreach my $field ($itemmarc->field($itemtag)){
1847             $field->add_subfields(@$unlinked_item_subfields);
1848         }
1849     }
1850         return $itemmarc;
1851 }
1852
1853 =head1 PRIVATE FUNCTIONS AND VARIABLES
1854
1855 The following functions are not meant to be called
1856 directly, but are documented in order to explain
1857 the inner workings of C<C4::Items>.
1858
1859 =cut
1860
1861 =head2 %derived_columns
1862
1863 This hash keeps track of item columns that
1864 are strictly derived from other columns in
1865 the item record and are not meant to be set
1866 independently.
1867
1868 Each key in the hash should be the name of a
1869 column (as named by TransformMarcToKoha).  Each
1870 value should be hashref whose keys are the
1871 columns on which the derived column depends.  The
1872 hashref should also contain a 'BUILDER' key
1873 that is a reference to a sub that calculates
1874 the derived value.
1875
1876 =cut
1877
1878 my %derived_columns = (
1879     'items.cn_sort' => {
1880         'itemcallnumber' => 1,
1881         'items.cn_source' => 1,
1882         'BUILDER' => \&_calc_items_cn_sort,
1883     }
1884 );
1885
1886 =head2 _set_derived_columns_for_add 
1887
1888   _set_derived_column_for_add($item);
1889
1890 Given an item hash representing a new item to be added,
1891 calculate any derived columns.  Currently the only
1892 such column is C<items.cn_sort>.
1893
1894 =cut
1895
1896 sub _set_derived_columns_for_add {
1897     my $item = shift;
1898
1899     foreach my $column (keys %derived_columns) {
1900         my $builder = $derived_columns{$column}->{'BUILDER'};
1901         my $source_values = {};
1902         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1903             next if $source_column eq 'BUILDER';
1904             $source_values->{$source_column} = $item->{$source_column};
1905         }
1906         $builder->($item, $source_values);
1907     }
1908 }
1909
1910 =head2 _set_derived_columns_for_mod 
1911
1912   _set_derived_column_for_mod($item);
1913
1914 Given an item hash representing a new item to be modified.
1915 calculate any derived columns.  Currently the only
1916 such column is C<items.cn_sort>.
1917
1918 This routine differs from C<_set_derived_columns_for_add>
1919 in that it needs to handle partial item records.  In other
1920 words, the caller of C<ModItem> may have supplied only one
1921 or two columns to be changed, so this function needs to
1922 determine whether any of the columns to be changed affect
1923 any of the derived columns.  Also, if a derived column
1924 depends on more than one column, but the caller is not
1925 changing all of then, this routine retrieves the unchanged
1926 values from the database in order to ensure a correct
1927 calculation.
1928
1929 =cut
1930
1931 sub _set_derived_columns_for_mod {
1932     my $item = shift;
1933
1934     foreach my $column (keys %derived_columns) {
1935         my $builder = $derived_columns{$column}->{'BUILDER'};
1936         my $source_values = {};
1937         my %missing_sources = ();
1938         my $must_recalc = 0;
1939         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1940             next if $source_column eq 'BUILDER';
1941             if (exists $item->{$source_column}) {
1942                 $must_recalc = 1;
1943                 $source_values->{$source_column} = $item->{$source_column};
1944             } else {
1945                 $missing_sources{$source_column} = 1;
1946             }
1947         }
1948         if ($must_recalc) {
1949             foreach my $source_column (keys %missing_sources) {
1950                 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1951             }
1952             $builder->($item, $source_values);
1953         }
1954     }
1955 }
1956
1957 =head2 _do_column_fixes_for_mod
1958
1959   _do_column_fixes_for_mod($item);
1960
1961 Given an item hashref containing one or more
1962 columns to modify, fix up certain values.
1963 Specifically, set to 0 any passed value
1964 of C<notforloan>, C<damaged>, C<itemlost>, or
1965 C<withdrawn> that is either undefined or
1966 contains the empty string.
1967
1968 =cut
1969
1970 sub _do_column_fixes_for_mod {
1971     my $item = shift;
1972
1973     if (exists $item->{'notforloan'} and
1974         (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1975         $item->{'notforloan'} = 0;
1976     }
1977     if (exists $item->{'damaged'} and
1978         (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1979         $item->{'damaged'} = 0;
1980     }
1981     if (exists $item->{'itemlost'} and
1982         (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1983         $item->{'itemlost'} = 0;
1984     }
1985     if (exists $item->{'withdrawn'} and
1986         (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1987         $item->{'withdrawn'} = 0;
1988     }
1989     if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1990         $item->{'permanent_location'} = $item->{'location'};
1991     }
1992     if (exists $item->{'timestamp'}) {
1993         delete $item->{'timestamp'};
1994     }
1995 }
1996
1997 =head2 _get_single_item_column
1998
1999   _get_single_item_column($column, $itemnumber);
2000
2001 Retrieves the value of a single column from an C<items>
2002 row specified by C<$itemnumber>.
2003
2004 =cut
2005
2006 sub _get_single_item_column {
2007     my $column = shift;
2008     my $itemnumber = shift;
2009     
2010     my $dbh = C4::Context->dbh;
2011     my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
2012     $sth->execute($itemnumber);
2013     my ($value) = $sth->fetchrow();
2014     return $value; 
2015 }
2016
2017 =head2 _calc_items_cn_sort
2018
2019   _calc_items_cn_sort($item, $source_values);
2020
2021 Helper routine to calculate C<items.cn_sort>.
2022
2023 =cut
2024
2025 sub _calc_items_cn_sort {
2026     my $item = shift;
2027     my $source_values = shift;
2028
2029     $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2030 }
2031
2032 =head2 _set_defaults_for_add 
2033
2034   _set_defaults_for_add($item_hash);
2035
2036 Given an item hash representing an item to be added, set
2037 correct default values for columns whose default value
2038 is not handled by the DBMS.  This includes the following
2039 columns:
2040
2041 =over 2
2042
2043 =item * 
2044
2045 C<items.dateaccessioned>
2046
2047 =item *
2048
2049 C<items.notforloan>
2050
2051 =item *
2052
2053 C<items.damaged>
2054
2055 =item *
2056
2057 C<items.itemlost>
2058
2059 =item *
2060
2061 C<items.withdrawn>
2062
2063 =back
2064
2065 =cut
2066
2067 sub _set_defaults_for_add {
2068     my $item = shift;
2069     $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
2070     $item->{$_} ||= 0 for (qw( notforloan damaged itemlost withdrawn));
2071 }
2072
2073 =head2 _koha_new_item
2074
2075   my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
2076
2077 Perform the actual insert into the C<items> table.
2078
2079 =cut
2080
2081 sub _koha_new_item {
2082     my ( $item, $barcode ) = @_;
2083     my $dbh=C4::Context->dbh;  
2084     my $error;
2085     my $query =
2086            "INSERT INTO items SET
2087             biblionumber        = ?,
2088             biblioitemnumber    = ?,
2089             barcode             = ?,
2090             dateaccessioned     = ?,
2091             booksellerid        = ?,
2092             homebranch          = ?,
2093             price               = ?,
2094             replacementprice    = ?,
2095             replacementpricedate = ?,
2096             datelastborrowed    = ?,
2097             datelastseen        = ?,
2098             stack               = ?,
2099             notforloan          = ?,
2100             damaged             = ?,
2101             itemlost            = ?,
2102             withdrawn            = ?,
2103             itemcallnumber      = ?,
2104             coded_location_qualifier = ?,
2105             restricted          = ?,
2106             itemnotes           = ?,
2107             holdingbranch       = ?,
2108             paidfor             = ?,
2109             location            = ?,
2110             permanent_location            = ?,
2111             onloan              = ?,
2112             issues              = ?,
2113             renewals            = ?,
2114             reserves            = ?,
2115             cn_source           = ?,
2116             cn_sort             = ?,
2117             ccode               = ?,
2118             itype               = ?,
2119             materials           = ?,
2120             uri = ?,
2121             enumchron           = ?,
2122             more_subfields_xml  = ?,
2123             copynumber          = ?,
2124             stocknumber         = ?
2125           ";
2126     my $sth = $dbh->prepare($query);
2127     my $today = C4::Dates->today('iso');
2128    $sth->execute(
2129             $item->{'biblionumber'},
2130             $item->{'biblioitemnumber'},
2131             $barcode,
2132             $item->{'dateaccessioned'},
2133             $item->{'booksellerid'},
2134             $item->{'homebranch'},
2135             $item->{'price'},
2136             $item->{'replacementprice'},
2137             $item->{'replacementpricedate'} || $today,
2138             $item->{datelastborrowed},
2139             $item->{datelastseen} || $today,
2140             $item->{stack},
2141             $item->{'notforloan'},
2142             $item->{'damaged'},
2143             $item->{'itemlost'},
2144             $item->{'withdrawn'},
2145             $item->{'itemcallnumber'},
2146             $item->{'coded_location_qualifier'},
2147             $item->{'restricted'},
2148             $item->{'itemnotes'},
2149             $item->{'holdingbranch'},
2150             $item->{'paidfor'},
2151             $item->{'location'},
2152             $item->{'permanent_location'},
2153             $item->{'onloan'},
2154             $item->{'issues'},
2155             $item->{'renewals'},
2156             $item->{'reserves'},
2157             $item->{'items.cn_source'},
2158             $item->{'items.cn_sort'},
2159             $item->{'ccode'},
2160             $item->{'itype'},
2161             $item->{'materials'},
2162             $item->{'uri'},
2163             $item->{'enumchron'},
2164             $item->{'more_subfields_xml'},
2165             $item->{'copynumber'},
2166             $item->{'stocknumber'},
2167     );
2168
2169     my $itemnumber;
2170     if ( defined $sth->errstr ) {
2171         $error.="ERROR in _koha_new_item $query".$sth->errstr;
2172     }
2173     else {
2174         $itemnumber = $dbh->{'mysql_insertid'};
2175     }
2176
2177     return ( $itemnumber, $error );
2178 }
2179
2180 =head2 MoveItemFromBiblio
2181
2182   MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2183
2184 Moves an item from a biblio to another
2185
2186 Returns undef if the move failed or the biblionumber of the destination record otherwise
2187
2188 =cut
2189
2190 sub MoveItemFromBiblio {
2191     my ($itemnumber, $frombiblio, $tobiblio) = @_;
2192     my $dbh = C4::Context->dbh;
2193     my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2194     $sth->execute( $tobiblio );
2195     my ( $tobiblioitem ) = $sth->fetchrow();
2196     $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2197     my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2198     if ($return == 1) {
2199         ModZebra( $tobiblio, "specialUpdate", "biblioserver" );
2200         ModZebra( $frombiblio, "specialUpdate", "biblioserver" );
2201             # Checking if the item we want to move is in an order 
2202         require C4::Acquisition;
2203         my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
2204             if ($order) {
2205                     # Replacing the biblionumber within the order if necessary
2206                     $order->{'biblionumber'} = $tobiblio;
2207                 C4::Acquisition::ModOrder($order);
2208             }
2209         return $tobiblio;
2210         }
2211     return;
2212 }
2213
2214 =head2 DelItemCheck
2215
2216    DelItemCheck($dbh, $biblionumber, $itemnumber);
2217
2218 Exported function (core API) for deleting an item record in Koha if there no current issue.
2219
2220 =cut
2221
2222 sub DelItemCheck {
2223     my ( $dbh, $biblionumber, $itemnumber ) = @_;
2224     my $error;
2225
2226         my $countanalytics=GetAnalyticsCount($itemnumber);
2227
2228
2229     # check that there is no issue on this item before deletion.
2230     my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2231     $sth->execute($itemnumber);
2232
2233     my $item = GetItem($itemnumber);
2234     my $onloan=$sth->fetchrow;
2235
2236     if ($onloan){
2237         $error = "book_on_loan" 
2238     }
2239     elsif ( !( C4::Context->userenv->{flags} & 1 )
2240         and C4::Context->preference("IndependentBranches")
2241         and ( C4::Context->userenv->{branch} ne $item->{'homebranch'} ) )
2242     {
2243         $error = "not_same_branch";
2244     }
2245         else{
2246         # check it doesnt have a waiting reserve
2247         $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2248         $sth->execute($itemnumber);
2249         my $reserve=$sth->fetchrow;
2250         if ($reserve){
2251             $error = "book_reserved";
2252         } elsif ($countanalytics > 0){
2253                 $error = "linked_analytics";
2254         } else {
2255             DelItem($dbh, $biblionumber, $itemnumber);
2256             return 1;
2257         }
2258     }
2259     return $error;
2260 }
2261
2262 =head2 _koha_modify_item
2263
2264   my ($itemnumber,$error) =_koha_modify_item( $item );
2265
2266 Perform the actual update of the C<items> row.  Note that this
2267 routine accepts a hashref specifying the columns to update.
2268
2269 =cut
2270
2271 sub _koha_modify_item {
2272     my ( $item ) = @_;
2273     my $dbh=C4::Context->dbh;  
2274     my $error;
2275
2276     my $query = "UPDATE items SET ";
2277     my @bind;
2278     for my $key ( keys %$item ) {
2279         next if ( $key eq 'itemnumber' );
2280         $query.="$key=?,";
2281         push @bind, $item->{$key};
2282     }
2283     $query =~ s/,$//;
2284     $query .= " WHERE itemnumber=?";
2285     push @bind, $item->{'itemnumber'};
2286     my $sth = C4::Context->dbh->prepare($query);
2287     $sth->execute(@bind);
2288     if ( C4::Context->dbh->errstr ) {
2289         $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2290         warn $error;
2291     }
2292     return ($item->{'itemnumber'},$error);
2293 }
2294
2295 =head2 _koha_delete_item
2296
2297   _koha_delete_item( $dbh, $itemnum );
2298
2299 Internal function to delete an item record from the koha tables
2300
2301 =cut
2302
2303 sub _koha_delete_item {
2304     my ( $dbh, $itemnum ) = @_;
2305
2306     # save the deleted item to deleteditems table
2307     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2308     $sth->execute($itemnum);
2309     my $data = $sth->fetchrow_hashref();
2310     my $query = "INSERT INTO deleteditems SET ";
2311     my @bind  = ();
2312     foreach my $key ( keys %$data ) {
2313         $query .= "$key = ?,";
2314         push( @bind, $data->{$key} );
2315     }
2316     $query =~ s/\,$//;
2317     $sth = $dbh->prepare($query);
2318     $sth->execute(@bind);
2319
2320     # delete from items table
2321     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2322     $sth->execute($itemnum);
2323     return;
2324 }
2325
2326 =head2 _marc_from_item_hash
2327
2328   my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2329
2330 Given an item hash representing a complete item record,
2331 create a C<MARC::Record> object containing an embedded
2332 tag representing that item.
2333
2334 The third, optional parameter C<$unlinked_item_subfields> is
2335 an arrayref of subfields (not mapped to C<items> fields per the
2336 framework) to be added to the MARC representation
2337 of the item.
2338
2339 =cut
2340
2341 sub _marc_from_item_hash {
2342     my $item = shift;
2343     my $frameworkcode = shift;
2344     my $unlinked_item_subfields;
2345     if (@_) {
2346         $unlinked_item_subfields = shift;
2347     }
2348    
2349     # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2350     # Also, don't emit a subfield if the underlying field is blank.
2351     my $mungeditem = { map {  (defined($item->{$_}) and $item->{$_} ne '') ? 
2352                                 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_})) 
2353                                 : ()  } keys %{ $item } }; 
2354
2355     my $item_marc = MARC::Record->new();
2356     foreach my $item_field ( keys %{$mungeditem} ) {
2357         my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2358         next unless defined $tag and defined $subfield;    # skip if not mapped to MARC field
2359         my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2360         foreach my $value (@values){
2361             if ( my $field = $item_marc->field($tag) ) {
2362                     $field->add_subfields( $subfield => $value );
2363             } else {
2364                 my $add_subfields = [];
2365                 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2366                     $add_subfields = $unlinked_item_subfields;
2367             }
2368             $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2369             }
2370         }
2371     }
2372
2373     return $item_marc;
2374 }
2375
2376 =head2 _repack_item_errors
2377
2378 Add an error message hash generated by C<CheckItemPreSave>
2379 to a list of errors.
2380
2381 =cut
2382
2383 sub _repack_item_errors {
2384     my $item_sequence_num = shift;
2385     my $item_ref = shift;
2386     my $error_ref = shift;
2387
2388     my @repacked_errors = ();
2389
2390     foreach my $error_code (sort keys %{ $error_ref }) {
2391         my $repacked_error = {};
2392         $repacked_error->{'item_sequence'} = $item_sequence_num;
2393         $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2394         $repacked_error->{'error_code'} = $error_code;
2395         $repacked_error->{'error_information'} = $error_ref->{$error_code};
2396         push @repacked_errors, $repacked_error;
2397     } 
2398
2399     return @repacked_errors;
2400 }
2401
2402 =head2 _get_unlinked_item_subfields
2403
2404   my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2405
2406 =cut
2407
2408 sub _get_unlinked_item_subfields {
2409     my $original_item_marc = shift;
2410     my $frameworkcode = shift;
2411
2412     my $marcstructure = GetMarcStructure(1, $frameworkcode);
2413
2414     # assume that this record has only one field, and that that
2415     # field contains only the item information
2416     my $subfields = [];
2417     my @fields = $original_item_marc->fields();
2418     if ($#fields > -1) {
2419         my $field = $fields[0];
2420             my $tag = $field->tag();
2421         foreach my $subfield ($field->subfields()) {
2422             if (defined $subfield->[1] and
2423                 $subfield->[1] ne '' and
2424                 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2425                 push @$subfields, $subfield->[0] => $subfield->[1];
2426             }
2427         }
2428     }
2429     return $subfields;
2430 }
2431
2432 =head2 _get_unlinked_subfields_xml
2433
2434   my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2435
2436 =cut
2437
2438 sub _get_unlinked_subfields_xml {
2439     my $unlinked_item_subfields = shift;
2440
2441     my $xml;
2442     if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2443         my $marc = MARC::Record->new();
2444         # use of tag 999 is arbitrary, and doesn't need to match the item tag
2445         # used in the framework
2446         $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2447         $marc->encoding("UTF-8");    
2448         $xml = $marc->as_xml("USMARC");
2449     }
2450
2451     return $xml;
2452 }
2453
2454 =head2 _parse_unlinked_item_subfields_from_xml
2455
2456   my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2457
2458 =cut
2459
2460 sub  _parse_unlinked_item_subfields_from_xml {
2461     my $xml = shift;
2462     require C4::Charset;
2463     return unless defined $xml and $xml ne "";
2464     my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8');
2465     my $unlinked_subfields = [];
2466     my @fields = $marc->fields();
2467     if ($#fields > -1) {
2468         foreach my $subfield ($fields[0]->subfields()) {
2469             push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2470         }
2471     }
2472     return $unlinked_subfields;
2473 }
2474
2475 =head2 GetAnalyticsCount
2476
2477   $count= &GetAnalyticsCount($itemnumber)
2478
2479 counts Usage of itemnumber in Analytical bibliorecords. 
2480
2481 =cut
2482
2483 sub GetAnalyticsCount {
2484     my ($itemnumber) = @_;
2485     require C4::Search;
2486
2487     ### ZOOM search here
2488     my $query;
2489     $query= "hi=".$itemnumber;
2490             my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
2491     return ($result);
2492 }
2493
2494 =head2 GetItemHolds
2495
2496 =over 4
2497 $holds = &GetItemHolds($biblionumber, $itemnumber);
2498
2499 =back
2500
2501 This function return the count of holds with $biblionumber and $itemnumber
2502
2503 =cut
2504
2505 sub GetItemHolds {
2506     my ($biblionumber, $itemnumber) = @_;
2507     my $holds;
2508     my $dbh            = C4::Context->dbh;
2509     my $query          = "SELECT count(*)
2510         FROM  reserves
2511         WHERE biblionumber=? AND itemnumber=?";
2512     my $sth = $dbh->prepare($query);
2513     $sth->execute($biblionumber, $itemnumber);
2514     $holds = $sth->fetchrow;
2515     return $holds;
2516 }
2517
2518 # Return the list of the column names of items table
2519 sub _get_items_columns {
2520     my $dbh = C4::Context->dbh;
2521     my $sth = $dbh->column_info(undef, undef, 'items', '%');
2522     $sth->execute;
2523     my $results = $sth->fetchall_hashref('COLUMN_NAME');
2524     return keys %$results;
2525 }
2526
2527 =head2 SearchItems
2528
2529     my $items = SearchItems($field, $value);
2530
2531 SearchItems will search for items on a specific given field.
2532 For instance you can search all items with a specific stocknumber like this:
2533
2534     my $items = SearchItems('stocknumber', $stocknumber);
2535
2536 =cut
2537
2538 sub SearchItems {
2539     my ($field, $value) = @_;
2540
2541     my $dbh = C4::Context->dbh;
2542     my @columns = _get_items_columns;
2543     my $results = [];
2544     if(0 < grep /^$field$/, @columns) {
2545         my $query = "SELECT $field FROM items WHERE $field = ?";
2546         my $sth = $dbh->prepare( $query );
2547         $sth->execute( $value );
2548         $results = $sth->fetchall_arrayref({});
2549     }
2550     return $results;
2551 }
2552
2553
2554 =head1  OTHER FUNCTIONS
2555
2556 =head2 _find_value
2557
2558   ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2559
2560 Find the given $subfield in the given $tag in the given
2561 MARC::Record $record.  If the subfield is found, returns
2562 the (indicators, value) pair; otherwise, (undef, undef) is
2563 returned.
2564
2565 PROPOSITION :
2566 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2567 I suggest we export it from this module.
2568
2569 =cut
2570
2571 sub _find_value {
2572     my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2573     my @result;
2574     my $indicator;
2575     if ( $tagfield < 10 ) {
2576         if ( $record->field($tagfield) ) {
2577             push @result, $record->field($tagfield)->data();
2578         } else {
2579             push @result, "";
2580         }
2581     } else {
2582         foreach my $field ( $record->field($tagfield) ) {
2583             my @subfields = $field->subfields();
2584             foreach my $subfield (@subfields) {
2585                 if ( @$subfield[0] eq $insubfield ) {
2586                     push @result, @$subfield[1];
2587                     $indicator = $field->indicator(1) . $field->indicator(2);
2588                 }
2589             }
2590         }
2591     }
2592     return ( $indicator, @result );
2593 }
2594
2595
2596 =head2 PrepareItemrecordDisplay
2597
2598   PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2599
2600 Returns a hash with all the fields for Display a given item data in a template
2601
2602 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2603
2604 =cut
2605
2606 sub PrepareItemrecordDisplay {
2607
2608     my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2609
2610     my $dbh = C4::Context->dbh;
2611     $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2612     my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2613     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2614
2615     # return nothing if we don't have found an existing framework.
2616     return q{} unless $tagslib;
2617     my $itemrecord;
2618     if ($itemnum) {
2619         $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
2620     }
2621     my @loop_data;
2622
2623     my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
2624     my $query = qq{
2625         SELECT authorised_value,lib FROM authorised_values
2626     };
2627     $query .= qq{
2628         LEFT JOIN authorised_values_branches ON ( id = av_id )
2629     } if $branch_limit;
2630     $query .= qq{
2631         WHERE category = ?
2632     };
2633     $query .= qq{ AND ( branchcode = ? OR branchcode IS NULL )} if $branch_limit;
2634     $query .= qq{ ORDER BY lib};
2635     my $authorised_values_sth = $dbh->prepare( $query );
2636     foreach my $tag ( sort keys %{$tagslib} ) {
2637         my $previous_tag = '';
2638         if ( $tag ne '' ) {
2639
2640             # loop through each subfield
2641             my $cntsubf;
2642             foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2643                 next if ( subfield_is_koha_internal_p($subfield) );
2644                 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2645                 my %subfield_data;
2646                 $subfield_data{tag}           = $tag;
2647                 $subfield_data{subfield}      = $subfield;
2648                 $subfield_data{countsubfield} = $cntsubf++;
2649                 $subfield_data{kohafield}     = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2650                 $subfield_data{id}            = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2651
2652                 #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2653                 $subfield_data{marc_lib}   = $tagslib->{$tag}->{$subfield}->{lib};
2654                 $subfield_data{mandatory}  = $tagslib->{$tag}->{$subfield}->{mandatory};
2655                 $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2656                 $subfield_data{hidden}     = "display:none"
2657                   if $tagslib->{$tag}->{$subfield}->{hidden};
2658                 my ( $x, $defaultvalue );
2659                 if ($itemrecord) {
2660                     ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2661                 }
2662                 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2663                 if ( !defined $defaultvalue ) {
2664                     $defaultvalue = q||;
2665                 } else {
2666                     $defaultvalue =~ s/"/&quot;/g;
2667                 }
2668
2669                 # search for itemcallnumber if applicable
2670                 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2671                     && C4::Context->preference('itemcallnumber') ) {
2672                     my $CNtag      = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2673                     my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2674                     if ( $itemrecord and my $field = $itemrecord->field($CNtag) ) {
2675                         $defaultvalue = $field->subfield($CNsubfield);
2676                     }
2677                 }
2678                 if (   $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2679                     && $defaultvalues
2680                     && $defaultvalues->{'callnumber'} ) {
2681                     if( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ){
2682                         # if the item record exists, only use default value if the item has no callnumber
2683                         $defaultvalue = $defaultvalues->{callnumber};
2684                     } elsif ( !$itemrecord and $defaultvalues ) {
2685                         # if the item record *doesn't* exists, always use the default value
2686                         $defaultvalue = $defaultvalues->{callnumber};
2687                     }
2688                 }
2689                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2690                     && $defaultvalues
2691                     && $defaultvalues->{'branchcode'} ) {
2692                     if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2693                         $defaultvalue = $defaultvalues->{branchcode};
2694                     }
2695                 }
2696                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2697                     && $defaultvalues
2698                     && $defaultvalues->{'location'} ) {
2699
2700                     if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2701                         # if the item record exists, only use default value if the item has no locationr
2702                         $defaultvalue = $defaultvalues->{location};
2703                     } elsif ( !$itemrecord and $defaultvalues ) {
2704                         # if the item record *doesn't* exists, always use the default value
2705                         $defaultvalue = $defaultvalues->{location};
2706                     }
2707                 }
2708                 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2709                     my @authorised_values;
2710                     my %authorised_lib;
2711
2712                     # builds list, depending on authorised value...
2713                     #---- branch
2714                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2715                         if (   ( C4::Context->preference("IndependentBranches") )
2716                             && ( C4::Context->userenv && C4::Context->userenv->{flags} % 2 != 1 ) ) {
2717                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2718                             $sth->execute( C4::Context->userenv->{branch} );
2719                             push @authorised_values, ""
2720                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2721                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2722                                 push @authorised_values, $branchcode;
2723                                 $authorised_lib{$branchcode} = $branchname;
2724                             }
2725                         } else {
2726                             my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2727                             $sth->execute;
2728                             push @authorised_values, ""
2729                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2730                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2731                                 push @authorised_values, $branchcode;
2732                                 $authorised_lib{$branchcode} = $branchname;
2733                             }
2734                         }
2735
2736                         $defaultvalue = C4::Context->userenv ? C4::Context->userenv->{branch} : undef;
2737                         if ( $defaultvalues and $defaultvalues->{branchcode} ) {
2738                             $defaultvalue = $defaultvalues->{branchcode};
2739                         }
2740
2741                         #----- itemtypes
2742                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2743                         my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2744                         $sth->execute;
2745                         push @authorised_values, ""
2746                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2747                         while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2748                             push @authorised_values, $itemtype;
2749                             $authorised_lib{$itemtype} = $description;
2750                         }
2751                         #---- class_sources
2752                     } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2753                         push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2754
2755                         my $class_sources = GetClassSources();
2756                         my $default_source = C4::Context->preference("DefaultClassificationSource");
2757
2758                         foreach my $class_source (sort keys %$class_sources) {
2759                             next unless $class_sources->{$class_source}->{'used'} or
2760                                         ($class_source eq $default_source);
2761                             push @authorised_values, $class_source;
2762                             $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2763                         }
2764
2765                         $defaultvalue = $default_source;
2766
2767                         #---- "true" authorised value
2768                     } else {
2769                         $authorised_values_sth->execute(
2770                             $tagslib->{$tag}->{$subfield}->{authorised_value},
2771                             $branch_limit ? $branch_limit : ()
2772                         );
2773                         push @authorised_values, ""
2774                           unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2775                         while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2776                             push @authorised_values, $value;
2777                             $authorised_lib{$value} = $lib;
2778                         }
2779                     }
2780                     $subfield_data{marc_value} = CGI::scrolling_list(
2781                         -name     => 'field_value',
2782                         -values   => \@authorised_values,
2783                         -default  => "$defaultvalue",
2784                         -labels   => \%authorised_lib,
2785                         -size     => 1,
2786                         -tabindex => '',
2787                         -multiple => 0,
2788                     );
2789                 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2790                         # opening plugin
2791                         my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2792                         if (do $plugin) {
2793                             my $extended_param = plugin_parameters( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2794                             my ( $function_name, $javascript ) = plugin_javascript( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2795                             $subfield_data{random}     = int(rand(1000000));    # why do we need 2 different randoms?
2796                             $subfield_data{marc_value} = qq[<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255"
2797                                 onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2798                                  onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2799                                 <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2800                                 $javascript];
2801                         } else {
2802                             warn "Plugin Failed: $plugin";
2803                             $subfield_data{marc_value} = qq(<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" />); # supply default input form
2804                         }
2805                 }
2806                 elsif ( $tag eq '' ) {       # it's an hidden field
2807                     $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2808                 }
2809                 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) {   # FIXME: shouldn't input type be "hidden" ?
2810                     $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2811                 }
2812                 elsif ( length($defaultvalue) > 100
2813                             or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2814                                   300 <= $tag && $tag < 400 && $subfield eq 'a' )
2815                             or (C4::Context->preference("marcflavour") eq "MARC21"  and
2816                                   500 <= $tag && $tag < 600                     )
2817                           ) {
2818                     # oversize field (textarea)
2819                     $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255">$defaultvalue</textarea>\n");
2820                 } else {
2821                     $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2822                 }
2823                 push( @loop_data, \%subfield_data );
2824             }
2825         }
2826     }
2827     my $itemnumber;
2828     if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
2829         $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
2830     }
2831     return {
2832         'itemtagfield'    => $itemtagfield,
2833         'itemtagsubfield' => $itemtagsubfield,
2834         'itemnumber'      => $itemnumber,
2835         'iteminformation' => \@loop_data
2836     };
2837 }
2838
2839 1;