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