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