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