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