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