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