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