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