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