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