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