removed 'use' that was causing redefined sub warning
[koha.git] / C4 / Items.pm
1 package C4::Items;
2
3 # Copyright 2007 LibLime, Inc.
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21
22 require Exporter;
23
24 use C4::Context;
25 use C4::Koha;
26 use C4::Biblio;
27 use C4::Dates qw/format_date format_date_in_iso/;
28 use MARC::Record;
29 use C4::ClassSource;
30 use C4::Log;
31
32 use vars qw($VERSION @ISA @EXPORT);
33
34 my $VERSION = 3.00;
35
36 @ISA = qw( Exporter );
37
38 # function exports
39 @EXPORT = qw(
40     GetItem
41     AddItemFromMarc
42     AddItem
43     ModItemFromMarc
44     ModItem
45     ModDateLastSeen
46     ModItemTransfer
47     DelItem
48
49     GetItemStatus
50     GetItemLocation
51     GetLostItems
52     GetItemsForInventory
53     GetItemsCount
54     GetItemInfosOf
55     GetItemsByBiblioitemnumber
56     GetItemsInfo
57     get_itemnumbers_of
58 );
59
60 =head1 NAME
61
62 C4::Items - item management functions
63
64 =head1 DESCRIPTION
65
66 This module contains an API for manipulating item 
67 records in Koha, and is used by cataloguing, circulation,
68 acquisitions, and serials management.
69
70 A Koha item record is stored in two places: the
71 items table and embedded in a MARC tag in the XML
72 version of the associated bib record in C<biblioitems.marcxml>.
73 This is done to allow the item information to be readily
74 indexed (e.g., by Zebra), but means that each item
75 modification transaction must keep the items table
76 and the MARC XML in sync at all times.
77
78 Consequently, all code that creates, modifies, or deletes
79 item records B<must> use an appropriate function from 
80 C<C4::Items>.  If no existing function is suitable, it is
81 better to add one to C<C4::Items> than to use add
82 one-off SQL statements to add or modify items.
83
84 The items table will be considered authoritative.  In other
85 words, if there is ever a discrepancy between the items
86 table and the MARC XML, the items table should be considered
87 accurate.
88
89 =head1 HISTORICAL NOTE
90
91 Most of the functions in C<C4::Items> were originally in
92 the C<C4::Biblio> module.
93
94 =head1 CORE EXPORTED FUNCTIONS
95
96 The following functions are meant for use by users
97 of C<C4::Items>
98
99 =cut
100
101 =head2 GetItem
102
103 =over 4
104
105 $item = GetItem($itemnumber,$barcode);
106
107 =back
108
109 Return item information, for a given itemnumber or barcode.
110 The return value is a hashref mapping item column
111 names to values.
112
113 =cut
114
115 sub GetItem {
116     my ($itemnumber,$barcode) = @_;
117     my $dbh = C4::Context->dbh;
118     if ($itemnumber) {
119         my $sth = $dbh->prepare("
120             SELECT * FROM items 
121             WHERE itemnumber = ?");
122         $sth->execute($itemnumber);
123         my $data = $sth->fetchrow_hashref;
124         return $data;
125     } else {
126         my $sth = $dbh->prepare("
127             SELECT * FROM items 
128             WHERE barcode = ?"
129             );
130         $sth->execute($barcode);
131         my $data = $sth->fetchrow_hashref;
132         return $data;
133     }
134 }    # sub GetItem
135
136 =head2 AddItemFromMarc
137
138 =over 4
139
140 my ($biblionumber, $biblioitemnumber, $itemnumber) 
141     = AddItemFromMarc($source_item_marc, $biblionumber);
142
143 =back
144
145 Given a MARC::Record object containing an embedded item
146 record and a biblionumber, create a new item record.
147
148 =cut
149
150 sub AddItemFromMarc {
151     my ( $source_item_marc, $biblionumber ) = @_;
152     my $dbh = C4::Context->dbh;
153
154     # parse item hash from MARC
155     my $frameworkcode = GetFrameworkCode( $biblionumber );
156     my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
157
158     return AddItem($item, $biblionumber, $dbh, $frameworkcode);
159 }
160
161 =head2 AddItem
162
163 =over 4
164
165 my ($biblionumber, $biblioitemnumber, $itemnumber) 
166     = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
167
168 =back
169
170 Given a hash containing item column names as keys,
171 create a new Koha item record.
172
173 The two optional parameters (C<$dbh> and C<$frameworkcode>)
174 do not need to be supplied for general use; they exist
175 simply to allow them to be picked up from AddItemFromMarc.
176
177 =cut
178
179 sub AddItem {
180     my $item = shift;
181     my $biblionumber = shift;
182
183     my $dbh           = @_ ? shift : C4::Context->dbh;
184     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
185
186     # needs old biblionumber and biblioitemnumber
187     $item->{'biblionumber'} = $biblionumber;
188     my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
189     $sth->execute( $item->{'biblionumber'} );
190     ($item->{'biblioitemnumber'}) = $sth->fetchrow;
191
192     _set_defaults_for_add($item);
193     _set_derived_columns_for_add($item);
194     # FIXME - checks here
195     my ( $itemnumber, $error ) = _koha_new_item( $dbh, $item, $item->{barcode} );
196     $item->{'itemnumber'} = $itemnumber;
197
198     # create MARC tag representing item and add to bib
199     my $new_item_marc = _marc_from_item_hash($item, $frameworkcode);
200     _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
201    
202     logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
203         if C4::Context->preference("CataloguingLog");
204     
205     return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
206 }
207
208 =head2 ModItemFromMarc
209
210 =over 4
211
212 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
213
214 =back
215
216 This function updates an item record based on a supplied
217 C<MARC::Record> object containing an embedded item field.
218 This API is meant for the use of C<additem.pl>; for 
219 other purposes, C<ModItem> should be used.
220
221 =cut
222
223 sub ModItemFromMarc {
224     my $item_marc = shift;
225     my $biblionumber = shift;
226     my $itemnumber = shift;
227
228     my $dbh = C4::Context->dbh;
229     my $frameworkcode = GetFrameworkCode( $biblionumber );
230     my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
231    
232     return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode); 
233 }
234
235 =head2 ModItem
236
237 =over 4
238
239 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
240
241 =back
242
243 Change one or more columns in an item record and update
244 the MARC representation of the item.
245
246 The first argument is a hashref mapping from item column
247 names to the new values.  The second and third arguments
248 are the biblionumber and itemnumber, respectively.
249
250 If one of the changed columns is used to calculate
251 the derived value of a column such as C<items.cn_sort>, 
252 this routine will perform the necessary calculation
253 and set the value.
254
255 =cut
256
257 sub ModItem {
258     my $item = shift;
259     my $biblionumber = shift;
260     my $itemnumber = shift;
261
262     # if $biblionumber is undefined, get it from the current item
263     unless (defined $biblionumber) {
264         $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
265     }
266
267     my $dbh           = @_ ? shift : C4::Context->dbh;
268     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
269
270     $item->{'itemnumber'} = $itemnumber;
271     _set_derived_columns_for_mod($item);
272     _do_column_fixes_for_mod($item);
273     # FIXME add checks
274     # duplicate barcode
275     # attempt to change itemnumber
276     # attempt to change biblionumber (if we want
277     # an API to relink an item to a different bib,
278     # it should be a separate function)
279
280     # update items table
281     _koha_modify_item($dbh, $item);
282
283     # update biblio MARC XML
284     my $whole_item = GetItem($itemnumber);
285     my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode);
286     _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
287     
288     logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
289         if C4::Context->preference("CataloguingLog");
290 }
291
292 =head2 ModItemTransfer
293
294 =over 4
295
296 ModItemTransfer($itenumber, $frombranch, $tobranch);
297
298 =back
299
300 Marks an item as being transferred from one branch
301 to another.
302
303 =cut
304
305 sub ModItemTransfer {
306     my ( $itemnumber, $frombranch, $tobranch ) = @_;
307
308     my $dbh = C4::Context->dbh;
309
310     #new entry in branchtransfers....
311     my $sth = $dbh->prepare(
312         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
313         VALUES (?, ?, NOW(), ?)");
314     $sth->execute($itemnumber, $frombranch, $tobranch);
315
316     ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
317     ModDateLastSeen($itemnumber);
318     return;
319 }
320
321 =head2 ModDateLastSeen
322
323 =over 4
324
325 ModDateLastSeen($itemnum);
326
327 =back
328
329 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
330 C<$itemnum> is the item number
331
332 =cut
333
334 sub ModDateLastSeen {
335     my ($itemnumber) = @_;
336     
337     my $today = C4::Dates->new();    
338     ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
339 }
340
341 =head2 DelItem
342
343 =over 4
344
345 DelItem($biblionumber, $itemnumber);
346
347 =back
348
349 Exported function (core API) for deleting an item record in Koha.
350
351 =cut
352
353 sub DelItem {
354     my ( $dbh, $biblionumber, $itemnumber ) = @_;
355     
356     # FIXME check the item has no current issues
357     
358     _koha_delete_item( $dbh, $itemnumber );
359
360     # get the MARC record
361     my $record = GetMarcBiblio($biblionumber);
362     my $frameworkcode = GetFrameworkCode($biblionumber);
363
364     # backup the record
365     my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
366     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
367
368     #search item field code
369     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
370     my @fields = $record->field($itemtag);
371
372     # delete the item specified
373     foreach my $field (@fields) {
374         if ( $field->subfield($itemsubfield) eq $itemnumber ) {
375             $record->delete_field($field);
376         }
377     }
378     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
379     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item") 
380         if C4::Context->preference("CataloguingLog");
381 }
382
383 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
384
385 The following functions provide various ways of 
386 getting an item record, a set of item records, or
387 lists of authorized values for certain item fields.
388
389 Some of the functions in this group are candidates
390 for refactoring -- for example, some of the code
391 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
392 has copy-and-paste work.
393
394 =cut
395
396 =head2 GetItemStatus
397
398 =over 4
399
400 $itemstatushash = GetItemStatus($fwkcode);
401
402 =back
403
404 Returns a list of valid values for the
405 C<items.notforloan> field.
406
407 NOTE: does B<not> return an individual item's
408 status.
409
410 Can be MARC dependant.
411 fwkcode is optional.
412 But basically could be can be loan or not
413 Create a status selector with the following code
414
415 =head3 in PERL SCRIPT
416
417 =over 4
418
419 my $itemstatushash = getitemstatus;
420 my @itemstatusloop;
421 foreach my $thisstatus (keys %$itemstatushash) {
422     my %row =(value => $thisstatus,
423                 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
424             );
425     push @itemstatusloop, \%row;
426 }
427 $template->param(statusloop=>\@itemstatusloop);
428
429 =back
430
431 =head3 in TEMPLATE
432
433 =over 4
434
435 <select name="statusloop">
436     <option value="">Default</option>
437 <!-- TMPL_LOOP name="statusloop" -->
438     <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
439 <!-- /TMPL_LOOP -->
440 </select>
441
442 =back
443
444 =cut
445
446 sub GetItemStatus {
447
448     # returns a reference to a hash of references to status...
449     my ($fwk) = @_;
450     my %itemstatus;
451     my $dbh = C4::Context->dbh;
452     my $sth;
453     $fwk = '' unless ($fwk);
454     my ( $tag, $subfield ) =
455       GetMarcFromKohaField( "items.notforloan", $fwk );
456     if ( $tag and $subfield ) {
457         my $sth =
458           $dbh->prepare(
459             "SELECT authorised_value
460             FROM marc_subfield_structure
461             WHERE tagfield=?
462                 AND tagsubfield=?
463                 AND frameworkcode=?
464             "
465           );
466         $sth->execute( $tag, $subfield, $fwk );
467         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
468             my $authvalsth =
469               $dbh->prepare(
470                 "SELECT authorised_value,lib
471                 FROM authorised_values 
472                 WHERE category=? 
473                 ORDER BY lib
474                 "
475               );
476             $authvalsth->execute($authorisedvaluecat);
477             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
478                 $itemstatus{$authorisedvalue} = $lib;
479             }
480             $authvalsth->finish;
481             return \%itemstatus;
482             exit 1;
483         }
484         else {
485
486             #No authvalue list
487             # build default
488         }
489         $sth->finish;
490     }
491
492     #No authvalue list
493     #build default
494     $itemstatus{"1"} = "Not For Loan";
495     return \%itemstatus;
496 }
497
498 =head2 GetItemLocation
499
500 =over 4
501
502 $itemlochash = GetItemLocation($fwk);
503
504 =back
505
506 Returns a list of valid values for the
507 C<items.location> field.
508
509 NOTE: does B<not> return an individual item's
510 location.
511
512 where fwk stands for an optional framework code.
513 Create a location selector with the following code
514
515 =head3 in PERL SCRIPT
516
517 =over 4
518
519 my $itemlochash = getitemlocation;
520 my @itemlocloop;
521 foreach my $thisloc (keys %$itemlochash) {
522     my $selected = 1 if $thisbranch eq $branch;
523     my %row =(locval => $thisloc,
524                 selected => $selected,
525                 locname => $itemlochash->{$thisloc},
526             );
527     push @itemlocloop, \%row;
528 }
529 $template->param(itemlocationloop => \@itemlocloop);
530
531 =back
532
533 =head3 in TEMPLATE
534
535 =over 4
536
537 <select name="location">
538     <option value="">Default</option>
539 <!-- TMPL_LOOP name="itemlocationloop" -->
540     <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
541 <!-- /TMPL_LOOP -->
542 </select>
543
544 =back
545
546 =cut
547
548 sub GetItemLocation {
549
550     # returns a reference to a hash of references to location...
551     my ($fwk) = @_;
552     my %itemlocation;
553     my $dbh = C4::Context->dbh;
554     my $sth;
555     $fwk = '' unless ($fwk);
556     my ( $tag, $subfield ) =
557       GetMarcFromKohaField( "items.location", $fwk );
558     if ( $tag and $subfield ) {
559         my $sth =
560           $dbh->prepare(
561             "SELECT authorised_value
562             FROM marc_subfield_structure 
563             WHERE tagfield=? 
564                 AND tagsubfield=? 
565                 AND frameworkcode=?"
566           );
567         $sth->execute( $tag, $subfield, $fwk );
568         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
569             my $authvalsth =
570               $dbh->prepare(
571                 "SELECT authorised_value,lib
572                 FROM authorised_values
573                 WHERE category=?
574                 ORDER BY lib"
575               );
576             $authvalsth->execute($authorisedvaluecat);
577             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
578                 $itemlocation{$authorisedvalue} = $lib;
579             }
580             $authvalsth->finish;
581             return \%itemlocation;
582             exit 1;
583         }
584         else {
585
586             #No authvalue list
587             # build default
588         }
589         $sth->finish;
590     }
591
592     #No authvalue list
593     #build default
594     $itemlocation{"1"} = "Not For Loan";
595     return \%itemlocation;
596 }
597
598 =head2 GetLostItems
599
600 =over 4
601
602 $items = GetLostItems($where,$orderby);
603
604 =back
605
606 This function get the items lost into C<$items>.
607
608 =over 2
609
610 =item input:
611 C<$where> is a hashref. it containts a field of the items table as key
612 and the value to match as value.
613 C<$orderby> is a field of the items table.
614
615 =item return:
616 C<$items> is a reference to an array full of hasref which keys are items' table column.
617
618 =item usage in the perl script:
619
620 my %where;
621 $where{barcode} = 0001548;
622 my $items = GetLostItems( \%where, "homebranch" );
623 $template->param(itemsloop => $items);
624
625 =back
626
627 =cut
628
629 sub GetLostItems {
630     # Getting input args.
631     my $where   = shift;
632     my $orderby = shift;
633     my $dbh     = C4::Context->dbh;
634
635     my $query   = "
636         SELECT *
637         FROM   items
638         WHERE  itemlost IS NOT NULL
639           AND  itemlost <> 0
640     ";
641     foreach my $key (keys %$where) {
642         $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
643     }
644     $query .= " ORDER BY ".$orderby if defined $orderby;
645
646     my $sth = $dbh->prepare($query);
647     $sth->execute;
648     my @items;
649     while ( my $row = $sth->fetchrow_hashref ){
650         push @items, $row;
651     }
652     return \@items;
653 }
654
655 =head2 GetItemsForInventory
656
657 =over 4
658
659 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
660
661 =back
662
663 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
664
665 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
666 It is ordered by callnumber,title.
667
668 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
669 the datelastseen can be used to specify that you want to see items not seen since a past date only.
670 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
671
672 =cut
673
674 sub GetItemsForInventory {
675     my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
676     my $dbh = C4::Context->dbh;
677     my $sth;
678     if ($datelastseen) {
679         $datelastseen=format_date_in_iso($datelastseen);  
680         my $query =
681                 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
682                  FROM items
683                    LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
684                  WHERE itemcallnumber>= ?
685                    AND itemcallnumber <=?
686                    AND (datelastseen< ? OR datelastseen IS NULL)";
687         $query.= " AND items.location=".$dbh->quote($location) if $location;
688         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
689         $query .= " ORDER BY itemcallnumber,title";
690         $sth = $dbh->prepare($query);
691         $sth->execute( $minlocation, $maxlocation, $datelastseen );
692     }
693     else {
694         my $query ="
695                 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
696                 FROM items 
697                   LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
698                 WHERE itemcallnumber>= ?
699                   AND itemcallnumber <=?";
700         $query.= " AND items.location=".$dbh->quote($location) if $location;
701         $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
702         $query .= " ORDER BY itemcallnumber,title";
703         $sth = $dbh->prepare($query);
704         $sth->execute( $minlocation, $maxlocation );
705     }
706     my @results;
707     while ( my $row = $sth->fetchrow_hashref ) {
708         $offset-- if ($offset);
709         $row->{datelastseen}=format_date($row->{datelastseen});
710         if ( ( !$offset ) && $size ) {
711             push @results, $row;
712             $size--;
713         }
714     }
715     return \@results;
716 }
717
718 =head2 GetItemsCount
719
720 =over 4
721 $count = &GetItemsCount( $biblionumber);
722
723 =back
724
725 This function return count of item with $biblionumber
726
727 =cut
728
729 sub GetItemsCount {
730     my ( $biblionumber ) = @_;
731     my $dbh = C4::Context->dbh;
732     my $query = "SELECT count(*)
733           FROM  items 
734           WHERE biblionumber=?";
735     my $sth = $dbh->prepare($query);
736     $sth->execute($biblionumber);
737     my $count = $sth->fetchrow;  
738     $sth->finish;
739     return ($count);
740 }
741
742 =head2 GetItemInfosOf
743
744 =over 4
745
746 GetItemInfosOf(@itemnumbers);
747
748 =back
749
750 =cut
751
752 sub GetItemInfosOf {
753     my @itemnumbers = @_;
754
755     my $query = '
756         SELECT *
757         FROM items
758         WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
759     ';
760     return get_infos_of( $query, 'itemnumber' );
761 }
762
763 =head2 GetItemsByBiblioitemnumber
764
765 =over 4
766
767 GetItemsByBiblioitemnumber($biblioitemnumber);
768
769 =back
770
771 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
772 Called by C<C4::XISBN>
773
774 =cut
775
776 sub GetItemsByBiblioitemnumber {
777     my ( $bibitem ) = @_;
778     my $dbh = C4::Context->dbh;
779     my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
780     # Get all items attached to a biblioitem
781     my $i = 0;
782     my @results; 
783     $sth->execute($bibitem) || die $sth->errstr;
784     while ( my $data = $sth->fetchrow_hashref ) {  
785         # Foreach item, get circulation information
786         my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
787                                    WHERE itemnumber = ?
788                                    AND returndate is NULL
789                                    AND issues.borrowernumber = borrowers.borrowernumber"
790         );
791         $sth2->execute( $data->{'itemnumber'} );
792         if ( my $data2 = $sth2->fetchrow_hashref ) {
793             # if item is out, set the due date and who it is out too
794             $data->{'date_due'}   = $data2->{'date_due'};
795             $data->{'cardnumber'} = $data2->{'cardnumber'};
796             $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
797         }
798         else {
799             # set date_due to blank, so in the template we check itemlost, and wthdrawn 
800             $data->{'date_due'} = '';                                                                                                         
801         }    # else         
802         $sth2->finish;
803         # Find the last 3 people who borrowed this item.                  
804         my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
805                       AND issues.borrowernumber = borrowers.borrowernumber
806                       AND returndate is not NULL
807                       ORDER BY returndate desc,timestamp desc LIMIT 3";
808         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
809         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
810         my $i2 = 0;
811         while ( my $data2 = $sth2->fetchrow_hashref ) {
812             $data->{"timestamp$i2"} = $data2->{'timestamp'};
813             $data->{"card$i2"}      = $data2->{'cardnumber'};
814             $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
815             $i2++;
816         }
817         $sth2->finish;
818         push(@results,$data);
819     } 
820     $sth->finish;
821     return (\@results); 
822 }
823
824 =head2 GetItemsInfo
825
826 =over 4
827
828 @results = GetItemsInfo($biblionumber, $type);
829
830 =back
831
832 Returns information about books with the given biblionumber.
833
834 C<$type> may be either C<intra> or anything else. If it is not set to
835 C<intra>, then the search will exclude lost, very overdue, and
836 withdrawn items.
837
838 C<GetItemsInfo> returns a list of references-to-hash. Each element
839 contains a number of keys. Most of them are table items from the
840 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
841 Koha database. Other keys include:
842
843 =over 2
844
845 =item C<$data-E<gt>{branchname}>
846
847 The name (not the code) of the branch to which the book belongs.
848
849 =item C<$data-E<gt>{datelastseen}>
850
851 This is simply C<items.datelastseen>, except that while the date is
852 stored in YYYY-MM-DD format in the database, here it is converted to
853 DD/MM/YYYY format. A NULL date is returned as C<//>.
854
855 =item C<$data-E<gt>{datedue}>
856
857 =item C<$data-E<gt>{class}>
858
859 This is the concatenation of C<biblioitems.classification>, the book's
860 Dewey code, and C<biblioitems.subclass>.
861
862 =item C<$data-E<gt>{ocount}>
863
864 I think this is the number of copies of the book available.
865
866 =item C<$data-E<gt>{order}>
867
868 If this is set, it is set to C<One Order>.
869
870 =back
871
872 =cut
873
874 sub GetItemsInfo {
875     my ( $biblionumber, $type ) = @_;
876     my $dbh   = C4::Context->dbh;
877     my $query = "SELECT *,items.notforloan as itemnotforloan
878                  FROM items 
879                  LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
880                  LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
881     $query .=  (C4::Context->preference('item-level_itypes')) ?
882                      " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
883                     : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
884     $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
885     my $sth = $dbh->prepare($query);
886     $sth->execute($biblionumber);
887     my $i = 0;
888     my @results;
889     my ( $date_due, $count_reserves );
890
891     my $isth    = $dbh->prepare(
892         "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
893         FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
894         WHERE  itemnumber = ?
895             AND returndate IS NULL"
896        );
897     while ( my $data = $sth->fetchrow_hashref ) {
898         my $datedue = '';
899         $isth->execute( $data->{'itemnumber'} );
900         if ( my $idata = $isth->fetchrow_hashref ) {
901             $data->{borrowernumber} = $idata->{borrowernumber};
902             $data->{cardnumber}     = $idata->{cardnumber};
903             $data->{surname}     = $idata->{surname};
904             $data->{firstname}     = $idata->{firstname};
905             $datedue                = $idata->{'date_due'};
906         if (C4::Context->preference("IndependantBranches")){
907         my $userenv = C4::Context->userenv;
908         if ( ($userenv) && ( $userenv->{flags} != 1 ) ) { 
909             $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
910         }
911         }
912         }
913         if ( $datedue eq '' ) {
914             my ( $restype, $reserves ) =
915               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
916             if ($restype) {
917                 $count_reserves = $restype;
918             }
919         }
920         $isth->finish;
921
922         #get branch information.....
923         my $bsth = $dbh->prepare(
924             "SELECT * FROM branches WHERE branchcode = ?
925         "
926         );
927         $bsth->execute( $data->{'holdingbranch'} );
928         if ( my $bdata = $bsth->fetchrow_hashref ) {
929             $data->{'branchname'} = $bdata->{'branchname'};
930         }
931         $data->{'datedue'}        = $datedue;
932         $data->{'count_reserves'} = $count_reserves;
933
934         # get notforloan complete status if applicable
935         my $sthnflstatus = $dbh->prepare(
936             'SELECT authorised_value
937             FROM   marc_subfield_structure
938             WHERE  kohafield="items.notforloan"
939         '
940         );
941
942         $sthnflstatus->execute;
943         my ($authorised_valuecode) = $sthnflstatus->fetchrow;
944         if ($authorised_valuecode) {
945             $sthnflstatus = $dbh->prepare(
946                 "SELECT lib FROM authorised_values
947                  WHERE  category=?
948                  AND authorised_value=?"
949             );
950             $sthnflstatus->execute( $authorised_valuecode,
951                 $data->{itemnotforloan} );
952             my ($lib) = $sthnflstatus->fetchrow;
953             $data->{notforloan} = $lib;
954         }
955
956         # my stack procedures
957         my $stackstatus = $dbh->prepare(
958             'SELECT authorised_value
959              FROM   marc_subfield_structure
960              WHERE  kohafield="items.stack"
961         '
962         );
963         $stackstatus->execute;
964
965         ($authorised_valuecode) = $stackstatus->fetchrow;
966         if ($authorised_valuecode) {
967             $stackstatus = $dbh->prepare(
968                 "SELECT lib
969                  FROM   authorised_values
970                  WHERE  category=?
971                  AND    authorised_value=?
972             "
973             );
974             $stackstatus->execute( $authorised_valuecode, $data->{stack} );
975             my ($lib) = $stackstatus->fetchrow;
976             $data->{stack} = $lib;
977         }
978         # Find the last 3 people who borrowed this item.
979         my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
980                                     WHERE itemnumber = ?
981                                     AND issues.borrowernumber = borrowers.borrowernumber
982                                     AND returndate IS NOT NULL LIMIT 3");
983         $sth2->execute($data->{'itemnumber'});
984         my $ii = 0;
985         while (my $data2 = $sth2->fetchrow_hashref()) {
986             $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
987             $data->{"card$ii"}      = $data2->{'cardnumber'} if $data2->{'cardnumber'};
988             $data->{"borrower$ii"}  = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
989             $ii++;
990         }
991
992         $results[$i] = $data;
993         $i++;
994     }
995     $sth->finish;
996
997     return (@results);
998 }
999
1000 =head2 get_itemnumbers_of
1001
1002 =over 4
1003
1004 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1005
1006 =back
1007
1008 Given a list of biblionumbers, return the list of corresponding itemnumbers
1009 for each biblionumber.
1010
1011 Return a reference on a hash where keys are biblionumbers and values are
1012 references on array of itemnumbers.
1013
1014 =cut
1015
1016 sub get_itemnumbers_of {
1017     my @biblionumbers = @_;
1018
1019     my $dbh = C4::Context->dbh;
1020
1021     my $query = '
1022         SELECT itemnumber,
1023             biblionumber
1024         FROM items
1025         WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1026     ';
1027     my $sth = $dbh->prepare($query);
1028     $sth->execute(@biblionumbers);
1029
1030     my %itemnumbers_of;
1031
1032     while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1033         push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1034     }
1035
1036     return \%itemnumbers_of;
1037 }
1038
1039 =head1 LIMITED USE FUNCTIONS
1040
1041 The following functions, while part of the public API,
1042 are not exported.  This is generally because they are
1043 meant to be used by only one script for a specific
1044 purpose, and should not be used in any other context
1045 without careful thought.
1046
1047 =cut
1048
1049 =head2 GetMarcItem
1050
1051 =over 4
1052
1053 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1054
1055 =back
1056
1057 Returns MARC::Record of the item passed in parameter.
1058 This function is meant for use only in C<cataloguing/additem.pl>,
1059 where it is needed to support that script's MARC-like
1060 editor.
1061
1062 =cut
1063
1064 sub GetMarcItem {
1065     my ( $biblionumber, $itemnumber ) = @_;
1066
1067     # GetMarcItem has been revised so that it does the following:
1068     #  1. Gets the item information from the items table.
1069     #  2. Converts it to a MARC field for storage in the bib record.
1070     #
1071     # The previous behavior was:
1072     #  1. Get the bib record.
1073     #  2. Return the MARC tag corresponding to the item record.
1074     #
1075     # The difference is that one treats the items row as authoritative,
1076     # while the other treats the MARC representation as authoritative
1077     # under certain circumstances.
1078
1079     my $itemrecord = GetItem($itemnumber);
1080
1081     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1082     # Also, don't emit a subfield if the underlying field is blank.
1083     my $mungeditem = { map {  $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  } keys %{ $itemrecord } };
1084
1085     my $itemmarc = TransformKohaToMarc($mungeditem);
1086     return $itemmarc;
1087
1088 }
1089
1090 =head1 PRIVATE FUNCTIONS AND VARIABLES
1091
1092 The following functions are not meant to be called
1093 directly, but are documented in order to explain
1094 the inner workings of C<C4::Items>.
1095
1096 =cut
1097
1098 =head2 %derived_columns
1099
1100 This hash keeps track of item columns that
1101 are strictly derived from other columns in
1102 the item record and are not meant to be set
1103 independently.
1104
1105 Each key in the hash should be the name of a
1106 column (as named by TransformMarcToKoha).  Each
1107 value should be hashref whose keys are the
1108 columns on which the derived column depends.  The
1109 hashref should also contain a 'BUILDER' key
1110 that is a reference to a sub that calculates
1111 the derived value.
1112
1113 =cut
1114
1115 my %derived_columns = (
1116     'items.cn_sort' => {
1117         'itemcallnumber' => 1,
1118         'items.cn_source' => 1,
1119         'BUILDER' => \&_calc_items_cn_sort,
1120     }
1121 );
1122
1123 =head2 _set_derived_columns_for_add 
1124
1125 =over 4
1126
1127 _set_derived_column_for_add($item);
1128
1129 =back
1130
1131 Given an item hash representing a new item to be added,
1132 calculate any derived columns.  Currently the only
1133 such column is C<items.cn_sort>.
1134
1135 =cut
1136
1137 sub _set_derived_columns_for_add {
1138     my $item = shift;
1139
1140     foreach my $column (keys %derived_columns) {
1141         my $builder = $derived_columns{$column}->{'BUILDER'};
1142         my $source_values = {};
1143         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1144             next if $source_column eq 'BUILDER';
1145             $source_values->{$source_column} = $item->{$source_column};
1146         }
1147         $builder->($item, $source_values);
1148     }
1149 }
1150
1151 =head2 _set_derived_columns_for_mod 
1152
1153 =over 4
1154
1155 _set_derived_column_for_mod($item);
1156
1157 =back
1158
1159 Given an item hash representing a new item to be modified.
1160 calculate any derived columns.  Currently the only
1161 such column is C<items.cn_sort>.
1162
1163 This routine differs from C<_set_derived_columns_for_add>
1164 in that it needs to handle partial item records.  In other
1165 words, the caller of C<ModItem> may have supplied only one
1166 or two columns to be changed, so this function needs to
1167 determine whether any of the columns to be changed affect
1168 any of the derived columns.  Also, if a derived column
1169 depends on more than one column, but the caller is not
1170 changing all of then, this routine retrieves the unchanged
1171 values from the database in order to ensure a correct
1172 calculation.
1173
1174 =cut
1175
1176 sub _set_derived_columns_for_mod {
1177     my $item = shift;
1178
1179     foreach my $column (keys %derived_columns) {
1180         my $builder = $derived_columns{$column}->{'BUILDER'};
1181         my $source_values = {};
1182         my %missing_sources = ();
1183         my $must_recalc = 0;
1184         foreach my $source_column (keys %{ $derived_columns{$column} }) {
1185             next if $source_column eq 'BUILDER';
1186             if (exists $item->{$source_column}) {
1187                 $must_recalc = 1;
1188                 $source_values->{$source_column} = $item->{$source_column};
1189             } else {
1190                 $missing_sources{$source_column} = 1;
1191             }
1192         }
1193         if ($must_recalc) {
1194             foreach my $source_column (keys %missing_sources) {
1195                 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1196             }
1197             $builder->($item, $source_values);
1198         }
1199     }
1200 }
1201
1202 =head2 _do_column_fixes_for_mod
1203
1204 =over 4
1205
1206 _do_column_fixes_for_mod($item);
1207
1208 =back
1209
1210 Given an item hashref containing one or more
1211 columns to modify, fix up certain values.
1212 Specifically, set to 0 any passed value
1213 of C<notforloan>, C<damaged>, C<itemlost>, or
1214 C<wthdrawn> that is either undefined or
1215 contains the empty string.
1216
1217 =cut
1218
1219 sub _do_column_fixes_for_mod {
1220     my $item = shift;
1221
1222     if (exists $item->{'notforloan'} and
1223         (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1224         $item->{'notforloan'} = 0;
1225     }
1226     if (exists $item->{'damaged'} and
1227         (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1228         $item->{'damaged'} = 0;
1229     }
1230     if (exists $item->{'itemlost'} and
1231         (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1232         $item->{'itemlost'} = 0;
1233     }
1234     if (exists $item->{'wthdrawn'} and
1235         (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1236         $item->{'wthdrawn'} = 0;
1237     }
1238 }
1239
1240 =head2 _get_single_item_column
1241
1242 =over 4
1243
1244 _get_single_item_column($column, $itemnumber);
1245
1246 =back
1247
1248 Retrieves the value of a single column from an C<items>
1249 row specified by C<$itemnumber>.
1250
1251 =cut
1252
1253 sub _get_single_item_column {
1254     my $column = shift;
1255     my $itemnumber = shift;
1256     
1257     my $dbh = C4::Context->dbh;
1258     my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1259     $sth->execute($itemnumber);
1260     my ($value) = $sth->fetchrow();
1261     return $value; 
1262 }
1263
1264 =head2 _calc_items_cn_sort
1265
1266 =over 4
1267
1268 _calc_items_cn_sort($item, $source_values);
1269
1270 =back
1271
1272 Helper routine to calculate C<items.cn_sort>.
1273
1274 =cut
1275
1276 sub _calc_items_cn_sort {
1277     my $item = shift;
1278     my $source_values = shift;
1279
1280     $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1281 }
1282
1283 =head2 _set_defaults_for_add 
1284
1285 =over 4
1286
1287 _set_defaults_for_add($item_hash);
1288
1289 =back
1290
1291 Given an item hash representing an item to be added, set
1292 correct default values for columns whose default value
1293 is not handled by the DBMS.  This includes the following
1294 columns:
1295
1296 =over 2
1297
1298 =item * 
1299
1300 C<items.dateaccessioned>
1301
1302 =item *
1303
1304 C<items.notforloan>
1305
1306 =item *
1307
1308 C<items.damaged>
1309
1310 =item *
1311
1312 C<items.itemlost>
1313
1314 =item *
1315
1316 C<items.wthdrawn>
1317
1318 =back
1319
1320 =cut
1321
1322 sub _set_defaults_for_add {
1323     my $item = shift;
1324
1325     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
1326     if (!(exists $item->{'dateaccessioned'}) || 
1327          ($item->{'dateaccessioned'} eq '')) {
1328         # FIXME add check for invalid date
1329         my $today = C4::Dates->new();    
1330         $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
1331     }
1332
1333     # various item status fields cannot be null
1334     $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
1335     $item->{'damaged'}    = 0 unless exists $item->{'damaged'}    and defined $item->{'damaged'};
1336     $item->{'itemlost'}   = 0 unless exists $item->{'itemlost'}   and defined $item->{'itemlost'};
1337     $item->{'wthdrawn'}   = 0 unless exists $item->{'wthdrawn'}   and defined $item->{'wthdrawn'};
1338 }
1339
1340 =head2 _koha_new_item
1341
1342 =over 4
1343
1344 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
1345
1346 =back
1347
1348 Perform the actual insert into the C<items> table.
1349
1350 =cut
1351
1352 sub _koha_new_item {
1353     my ( $dbh, $item, $barcode ) = @_;
1354     my $error;
1355
1356     my $query = 
1357            "INSERT INTO items SET
1358             biblionumber        = ?,
1359             biblioitemnumber    = ?,
1360             barcode             = ?,
1361             dateaccessioned     = ?,
1362             booksellerid        = ?,
1363             homebranch          = ?,
1364             price               = ?,
1365             replacementprice    = ?,
1366             replacementpricedate = NOW(),
1367             datelastborrowed    = ?,
1368             datelastseen        = NOW(),
1369             stack               = ?,
1370             notforloan          = ?,
1371             damaged             = ?,
1372             itemlost            = ?,
1373             wthdrawn            = ?,
1374             itemcallnumber      = ?,
1375             restricted          = ?,
1376             itemnotes           = ?,
1377             holdingbranch       = ?,
1378             paidfor             = ?,
1379             location            = ?,
1380             onloan              = ?,
1381             issues              = ?,
1382             renewals            = ?,
1383             reserves            = ?,
1384             cn_source           = ?,
1385             cn_sort             = ?,
1386             ccode               = ?,
1387             itype               = ?,
1388             materials           = ?,
1389             uri                 = ?
1390           ";
1391     my $sth = $dbh->prepare($query);
1392     $sth->execute(
1393             $item->{'biblionumber'},
1394             $item->{'biblioitemnumber'},
1395             $barcode,
1396             $item->{'dateaccessioned'},
1397             $item->{'booksellerid'},
1398             $item->{'homebranch'},
1399             $item->{'price'},
1400             $item->{'replacementprice'},
1401             $item->{datelastborrowed},
1402             $item->{stack},
1403             $item->{'notforloan'},
1404             $item->{'damaged'},
1405             $item->{'itemlost'},
1406             $item->{'wthdrawn'},
1407             $item->{'itemcallnumber'},
1408             $item->{'restricted'},
1409             $item->{'itemnotes'},
1410             $item->{'holdingbranch'},
1411             $item->{'paidfor'},
1412             $item->{'location'},
1413             $item->{'onloan'},
1414             $item->{'issues'},
1415             $item->{'renewals'},
1416             $item->{'reserves'},
1417             $item->{'items.cn_source'},
1418             $item->{'items.cn_sort'},
1419             $item->{'ccode'},
1420             $item->{'itype'},
1421             $item->{'materials'},
1422             $item->{'uri'},
1423     );
1424     my $itemnumber = $dbh->{'mysql_insertid'};
1425     if ( defined $sth->errstr ) {
1426         $error.="ERROR in _koha_new_item $query".$sth->errstr;
1427     }
1428     $sth->finish();
1429     return ( $itemnumber, $error );
1430 }
1431
1432 =head2 _koha_modify_item
1433
1434 =over 4
1435
1436 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
1437
1438 =back
1439
1440 Perform the actual update of the C<items> row.  Note that this
1441 routine accepts a hashref specifying the columns to update.
1442
1443 =cut
1444
1445 sub _koha_modify_item {
1446     my ( $dbh, $item ) = @_;
1447     my $error;
1448
1449     my $query = "UPDATE items SET ";
1450     my @bind;
1451     for my $key ( keys %$item ) {
1452         $query.="$key=?,";
1453         push @bind, $item->{$key};
1454     }
1455     $query =~ s/,$//;
1456     $query .= " WHERE itemnumber=?";
1457     push @bind, $item->{'itemnumber'};
1458     my $sth = $dbh->prepare($query);
1459     $sth->execute(@bind);
1460     if ( $dbh->errstr ) {
1461         $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
1462         warn $error;
1463     }
1464     $sth->finish();
1465     return ($item->{'itemnumber'},$error);
1466 }
1467
1468 =head2 _koha_delete_item
1469
1470 =over 4
1471
1472 _koha_delete_item( $dbh, $itemnum );
1473
1474 =back
1475
1476 Internal function to delete an item record from the koha tables
1477
1478 =cut
1479
1480 sub _koha_delete_item {
1481     my ( $dbh, $itemnum ) = @_;
1482
1483     # save the deleted item to deleteditems table
1484     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
1485     $sth->execute($itemnum);
1486     my $data = $sth->fetchrow_hashref();
1487     $sth->finish();
1488     my $query = "INSERT INTO deleteditems SET ";
1489     my @bind  = ();
1490     foreach my $key ( keys %$data ) {
1491         $query .= "$key = ?,";
1492         push( @bind, $data->{$key} );
1493     }
1494     $query =~ s/\,$//;
1495     $sth = $dbh->prepare($query);
1496     $sth->execute(@bind);
1497     $sth->finish();
1498
1499     # delete from items table
1500     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
1501     $sth->execute($itemnum);
1502     $sth->finish();
1503     return undef;
1504 }
1505
1506 =head2 _marc_from_item_hash
1507
1508 =over 4
1509
1510 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
1511
1512 =back
1513
1514 Given an item hash representing a complete item record,
1515 create a C<MARC::Record> object containing an embedded
1516 tag representing that item.
1517
1518 =cut
1519
1520 sub _marc_from_item_hash {
1521     my $item = shift;
1522     my $frameworkcode = shift;
1523    
1524     # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
1525     # Also, don't emit a subfield if the underlying field is blank.
1526     my $mungeditem = { map {  (defined($item->{$_}) and $item->{$_} ne '') ? 
1527                                 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_})) 
1528                                 : ()  } keys %{ $item } }; 
1529
1530     my $item_marc = MARC::Record->new();
1531     foreach my $item_field (keys %{ $mungeditem }) {
1532         my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
1533         next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
1534         if (my $field = $item_marc->field($tag)) {
1535             $field->add_subfields($subfield => $mungeditem->{$item_field});
1536         } else {
1537             $item_marc->add_fields( $tag, " ", " ", $subfield =>  $mungeditem->{$item_field});
1538         }
1539     }
1540
1541     return $item_marc;
1542 }
1543
1544 =head2 _add_item_field_to_biblio
1545
1546 =over 4
1547
1548 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
1549
1550 =back
1551
1552 Adds the fields from a MARC record containing the
1553 representation of a Koha item record to the MARC
1554 biblio record.  The input C<$item_marc> record
1555 is expect to contain just one field, the embedded
1556 item information field.
1557
1558 =cut
1559
1560 sub _add_item_field_to_biblio {
1561     my ($item_marc, $biblionumber, $frameworkcode) = @_;
1562
1563     my $biblio_marc = GetMarcBiblio($biblionumber);
1564
1565     foreach my $field ($item_marc->fields()) {
1566         $biblio_marc->append_fields($field);
1567     }
1568
1569     ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
1570 }
1571
1572 =head2 _replace_item_field_in_biblio
1573
1574 =over
1575
1576 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
1577
1578 =back
1579
1580 Given a MARC::Record C<$item_marc> containing one tag with the MARC 
1581 representation of the item, examine the biblio MARC
1582 for the corresponding tag for that item and 
1583 replace it with the tag from C<$item_marc>.
1584
1585 =cut
1586
1587 sub _replace_item_field_in_biblio {
1588     my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
1589     my $dbh = C4::Context->dbh;
1590     
1591     # get complete MARC record & replace the item field by the new one
1592     my $completeRecord = GetMarcBiblio($biblionumber);
1593     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
1594     my $itemField = $ItemRecord->field($itemtag);
1595     my @items = $completeRecord->field($itemtag);
1596     my $found = 0;
1597     foreach (@items) {
1598         if ($_->subfield($itemsubfield) eq $itemnumber) {
1599             $_->replace_with($itemField);
1600             $found = 1;
1601         }
1602     }
1603   
1604     unless ($found) { 
1605         # If we haven't found the matching field,
1606         # just add it.  However, this means that
1607         # there is likely a bug.
1608         $completeRecord->append_fields($itemField);
1609     }
1610
1611     # save the record
1612     ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
1613 }
1614
1615 1;