3 # Copyright 2007 LibLime, Inc.
5 # This file is part of Koha.
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
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.
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
31 use vars qw($VERSION @ISA @EXPORT);
35 @ISA = qw( Exporter );
50 C4::Items - item management functions
54 This module contains an API for manipulating item
55 records in Koha, and is used by cataloguing, circulation,
56 acquisitions, and serials management.
58 A Koha item record is stored in two places: the
59 items table and embedded in a MARC tag in the XML
60 version of the associated bib record in C<biblioitems.marcxml>.
61 This is done to allow the item information to be readily
62 indexed (e.g., by Zebra), but means that each item
63 modification transaction must keep the items table
64 and the MARC XML in sync at all times.
66 Consequently, all code that creates, modifies, or deletes
67 item records B<must> use an appropriate function from
68 C<C4::Items>. If no existing function is suitable, it is
69 better to add one to C<C4::Items> than to use add
70 one-off SQL statements to add or modify items.
72 The items table will be considered authoritative. In other
73 words, if there is ever a discrepancy between the items
74 table and the MARC XML, the items table should be considered
77 =head1 HISTORICAL NOTE
79 Most of the functions in C<C4::Items> were originally in
80 the C<C4::Biblio> module.
82 =head1 EXPORTED FUNCTIONS
84 The following functions are meant for use by users
93 $item = GetItem($itemnumber,$barcode);
97 Return item information, for a given itemnumber or barcode.
98 The return value is a hashref mapping item column
104 my ($itemnumber,$barcode) = @_;
105 my $dbh = C4::Context->dbh;
107 my $sth = $dbh->prepare("
109 WHERE itemnumber = ?");
110 $sth->execute($itemnumber);
111 my $data = $sth->fetchrow_hashref;
114 my $sth = $dbh->prepare("
118 $sth->execute($barcode);
119 my $data = $sth->fetchrow_hashref;
124 =head2 AddItemFromMarc
128 my ($biblionumber, $biblioitemnumber, $itemnumber)
129 = AddItemFromMarc($source_item_marc, $biblionumber);
133 Given a MARC::Record object containing an embedded item
134 record and a biblionumber, create a new item record.
138 sub AddItemFromMarc {
139 my ( $source_item_marc, $biblionumber ) = @_;
140 my $dbh = C4::Context->dbh;
142 # parse item hash from MARC
143 my $frameworkcode = GetFrameworkCode( $biblionumber );
144 my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
146 return AddItem($item, $biblionumber, $dbh, $frameworkcode);
153 my ($biblionumber, $biblioitemnumber, $itemnumber)
154 = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
158 Given a hash containing item column names as keys,
159 create a new Koha item record.
161 The two optional parameters (C<$dbh> and C<$frameworkcode>)
162 do not need to be supplied for general use; they exist
163 simply to allow them to be picked up from AddItemFromMarc.
169 my $biblionumber = shift;
171 my $dbh = @_ ? shift : C4::Context->dbh;
172 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
174 # needs old biblionumber and biblioitemnumber
175 $item->{'biblionumber'} = $biblionumber;
176 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
177 $sth->execute( $item->{'biblionumber'} );
178 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
180 _set_defaults_for_add($item);
181 _set_derived_columns_for_add($item);
182 # FIXME - checks here
183 my ( $itemnumber, $error ) = _koha_new_item( $dbh, $item, $item->{barcode} );
184 $item->{'itemnumber'} = $itemnumber;
186 # create MARC tag representing item and add to bib
187 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode);
188 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
190 logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
191 if C4::Context->preference("CataloguingLog");
193 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
196 =head2 ModItemFromMarc
200 sub ModItemFromMarc {
201 my $item_marc = shift;
202 my $biblionumber = shift;
203 my $itemnumber = shift;
205 my $dbh = C4::Context->dbh;
206 my $frameworkcode = GetFrameworkCode( $biblionumber );
207 my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
209 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode);
218 my $biblionumber = shift;
219 my $itemnumber = shift;
221 # if $biblionumber is undefined, get it from the current item
222 unless (defined $biblionumber) {
223 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
226 my $dbh = @_ ? shift : C4::Context->dbh;
227 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
229 $item->{'itemnumber'} = $itemnumber;
230 _set_derived_columns_for_mod($item);
231 _do_column_fixes_for_mod($item);
235 _koha_modify_item($dbh, $item);
237 # update biblio MARC XML
238 my $whole_item = GetItem($itemnumber);
239 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode);
240 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
242 logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
243 if C4::Context->preference("CataloguingLog");
246 =head2 ModItemTransfer
250 sub ModItemTransfer {
251 my ( $itemnumber, $frombranch, $tobranch ) = @_;
253 my $dbh = C4::Context->dbh;
255 #new entry in branchtransfers....
256 my $sth = $dbh->prepare(
257 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
258 VALUES (?, ?, NOW(), ?)");
259 $sth->execute($itemnumber, $frombranch, $tobranch);
261 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
262 ModDateLastSeen($itemnumber);
266 =head2 ModDateLastSeen
270 ModDateLastSeen($itemnum);
274 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
275 C<$itemnum> is the item number
279 sub ModDateLastSeen {
280 my ($itemnumber) = @_;
282 my $today = C4::Dates->new();
283 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
286 =head1 LIMITED USE FUNCTIONS
288 The following functions, while part of the public API,
289 are not exported. This is generally because they are
290 meant to be used by only one script for a specific
291 purpose, and should not be used in any other context
292 without careful thought.
300 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
304 Returns MARC::Record of the item passed in parameter.
305 This function is meant for use only in C<cataloguing/additem.pl>,
306 where it is needed to support that script's MARC-like
312 my ( $biblionumber, $itemnumber ) = @_;
314 # GetMarcItem has been revised so that it does the following:
315 # 1. Gets the item information from the items table.
316 # 2. Converts it to a MARC field for storage in the bib record.
318 # The previous behavior was:
319 # 1. Get the bib record.
320 # 2. Return the MARC tag corresponding to the item record.
322 # The difference is that one treats the items row as authoritative,
323 # while the other treats the MARC representation as authoritative
324 # under certain circumstances.
326 my $itemrecord = GetItem($itemnumber);
328 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
329 # Also, don't emit a subfield if the underlying field is blank.
330 my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
332 my $itemmarc = TransformKohaToMarc($mungeditem);
337 =head1 PRIVATE FUNCTIONS AND VARIABLES
339 The following functions are not meant to be called
340 directly, but are documented in order to explain
341 the inner workings of C<C4::Items>.
345 =head2 %derived_columns
347 This hash keeps track of item columns that
348 are strictly derived from other columns in
349 the item record and are not meant to be set
352 Each key in the hash should be the name of a
353 column (as named by TransformMarcToKoha). Each
354 value should be hashref whose keys are the
355 columns on which the derived column depends. The
356 hashref should also contain a 'BUILDER' key
357 that is a reference to a sub that calculates
362 my %derived_columns = (
364 'itemcallnumber' => 1,
365 'items.cn_source' => 1,
366 'BUILDER' => \&_calc_items_cn_sort,
370 =head2 _set_derived_columns_for_add
374 _set_derived_column_for_add($item);
378 Given an item hash representing a new item to be added,
379 calculate any derived columns. Currently the only
380 such column is C<items.cn_sort>.
384 sub _set_derived_columns_for_add {
387 foreach my $column (keys %derived_columns) {
388 my $builder = $derived_columns{$column}->{'BUILDER'};
389 my $source_values = {};
390 foreach my $source_column (keys %{ $derived_columns{$column} }) {
391 next if $source_column eq 'BUILDER';
392 $source_values->{$source_column} = $item->{$source_column};
394 $builder->($item, $source_values);
398 =head2 _set_derived_columns_for_mod
402 _set_derived_column_for_mod($item);
406 Given an item hash representing a new item to be modified.
407 calculate any derived columns. Currently the only
408 such column is C<items.cn_sort>.
410 This routine differs from C<_set_derived_columns_for_add>
411 in that it needs to handle partial item records. In other
412 words, the caller of C<ModItem> may have supplied only one
413 or two columns to be changed, so this function needs to
414 determine whether any of the columns to be changed affect
415 any of the derived columns. Also, if a derived column
416 depends on more than one column, but the caller is not
417 changing all of then, this routine retrieves the unchanged
418 values from the database in order to ensure a correct
423 sub _set_derived_columns_for_mod {
426 foreach my $column (keys %derived_columns) {
427 my $builder = $derived_columns{$column}->{'BUILDER'};
428 my $source_values = {};
429 my %missing_sources = ();
431 foreach my $source_column (keys %{ $derived_columns{$column} }) {
432 next if $source_column eq 'BUILDER';
433 if (exists $item->{$source_column}) {
435 $source_values->{$source_column} = $item->{$source_column};
437 $missing_sources{$source_column} = 1;
441 foreach my $source_column (keys %missing_sources) {
442 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
444 $builder->($item, $source_values);
449 =head2 _do_column_fixes_for_mod
453 _do_column_fixes_for_mod($item);
457 Given an item hashref containing one or more
458 columns to modify, fix up certain values.
459 Specifically, set to 0 any passed value
460 of C<notforloan>, C<damaged>, C<itemlost>, or
461 C<wthdrawn> that is either undefined or
462 contains the empty string.
466 sub _do_column_fixes_for_mod {
469 if (exists $item->{'notforloan'} and
470 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
471 $item->{'notforloan'} = 0;
473 if (exists $item->{'damaged'} and
474 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
475 $item->{'damaged'} = 0;
477 if (exists $item->{'itemlost'} and
478 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
479 $item->{'itemlost'} = 0;
481 if (exists $item->{'wthdrawn'} and
482 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
483 $item->{'wthdrawn'} = 0;
487 =head2 _get_single_item_column
491 _get_single_item_column($column, $itemnumber);
495 Retrieves the value of a single column from an C<items>
496 row specified by C<$itemnumber>.
500 sub _get_single_item_column {
502 my $itemnumber = shift;
504 my $dbh = C4::Context->dbh;
505 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
506 $sth->execute($itemnumber);
507 my ($value) = $sth->fetchrow();
511 =head2 _calc_items_cn_sort
515 _calc_items_cn_sort($item, $source_values);
519 Helper routine to calculate C<items.cn_sort>.
523 sub _calc_items_cn_sort {
525 my $source_values = shift;
527 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
530 =head2 _set_defaults_for_add
534 _set_defaults_for_add($item_hash);
538 Given an item hash representing an item to be added, set
539 correct default values for columns whose default value
540 is not handled by the DBMS. This includes the following
547 C<items.dateaccessioned>
569 sub _set_defaults_for_add {
572 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
573 if (!(exists $item->{'dateaccessioned'}) ||
574 ($item->{'dateaccessioned'} eq '')) {
575 # FIXME add check for invalid date
576 my $today = C4::Dates->new();
577 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
580 # various item status fields cannot be null
581 $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
582 $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'};
583 $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'};
584 $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'};
587 =head2 _koha_new_item
591 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
598 my ( $dbh, $item, $barcode ) = @_;
602 "INSERT INTO items SET
604 biblioitemnumber = ?,
610 replacementprice = ?,
611 replacementpricedate = NOW(),
612 datelastborrowed = ?,
613 datelastseen = NOW(),
636 my $sth = $dbh->prepare($query);
638 $item->{'biblionumber'},
639 $item->{'biblioitemnumber'},
641 $item->{'dateaccessioned'},
642 $item->{'booksellerid'},
643 $item->{'homebranch'},
645 $item->{'replacementprice'},
646 $item->{datelastborrowed},
648 $item->{'notforloan'},
652 $item->{'itemcallnumber'},
653 $item->{'restricted'},
654 $item->{'itemnotes'},
655 $item->{'holdingbranch'},
662 $item->{'items.cn_source'},
663 $item->{'items.cn_sort'},
666 $item->{'materials'},
669 my $itemnumber = $dbh->{'mysql_insertid'};
670 if ( defined $sth->errstr ) {
671 $error.="ERROR in _koha_new_item $query".$sth->errstr;
674 return ( $itemnumber, $error );
677 =head2 _koha_modify_item
681 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
687 sub _koha_modify_item {
688 my ( $dbh, $item ) = @_;
691 my $query = "UPDATE items SET ";
693 for my $key ( keys %$item ) {
695 push @bind, $item->{$key};
698 $query .= " WHERE itemnumber=?";
699 push @bind, $item->{'itemnumber'};
700 my $sth = $dbh->prepare($query);
701 $sth->execute(@bind);
702 if ( $dbh->errstr ) {
703 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
707 return ($item->{'itemnumber'},$error);
710 =head2 _marc_from_item_hash
714 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
718 Given an item hash representing a complete item record,
719 create a C<MARC::Record> object containing an embedded
720 tag representing that item.
724 sub _marc_from_item_hash {
726 my $frameworkcode = shift;
728 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
729 # Also, don't emit a subfield if the underlying field is blank.
730 my $mungeditem = { map { $item->{$_} ne '' ?
731 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
732 : () } keys %{ $item } };
734 my $item_marc = MARC::Record->new();
735 foreach my $item_field (keys %{ $mungeditem }) {
736 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
737 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
738 if (my $field = $item_marc->field($tag)) {
739 $field->add_subfields($subfield => $mungeditem->{$item_field});
741 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field});
748 =head2 _add_item_field_to_biblio
752 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
756 Adds the fields from a MARC record containing the
757 representation of a Koha item record to the MARC
758 biblio record. The input C<$item_marc> record
759 is expect to contain just one field, the embedded
760 item information field.
764 sub _add_item_field_to_biblio {
765 my ($item_marc, $biblionumber, $frameworkcode) = @_;
767 my $biblio_marc = GetMarcBiblio($biblionumber);
769 foreach my $field ($item_marc->fields()) {
770 $biblio_marc->append_fields($field);
773 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
776 =head2 _replace_item_field_in_biblio
780 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
784 Given a MARC::Record C<$item_marc> containing one tag with the MARC
785 representation of the item, examine the biblio MARC
786 for the corresponding tag for that item and
787 replace it with the tag from C<$item_marc>.
791 sub _replace_item_field_in_biblio {
792 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
793 my $dbh = C4::Context->dbh;
795 # get complete MARC record & replace the item field by the new one
796 my $completeRecord = GetMarcBiblio($biblionumber);
797 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
798 my $itemField = $ItemRecord->field($itemtag);
799 my @items = $completeRecord->field($itemtag);
802 if ($_->subfield($itemsubfield) eq $itemnumber) {
803 $_->replace_with($itemField);
809 # If we haven't found the matching field,
810 # just add it. However, this means that
811 # there is likely a bug.
812 $completeRecord->append_fields($itemField);
816 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);