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 Returns MARC::Record of the item passed in parameter.
301 This function is meant for use only in C<cataloguing/additem.pl>,
302 where it is needed to support that script's MARC-like
310 my ( $biblionumber, $itemnumber ) = @_;
312 # GetMarcItem has been revised so that it does the following:
313 # 1. Gets the item information from the items table.
314 # 2. Converts it to a MARC field for storage in the bib record.
316 # The previous behavior was:
317 # 1. Get the bib record.
318 # 2. Return the MARC tag corresponding to the item record.
320 # The difference is that one treats the items row as authoritative,
321 # while the other treats the MARC representation as authoritative
322 # under certain circumstances.
324 my $itemrecord = GetItem($itemnumber);
326 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
327 # Also, don't emit a subfield if the underlying field is blank.
328 my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
330 my $itemmarc = TransformKohaToMarc($mungeditem);
335 =head1 PRIVATE FUNCTIONS AND VARIABLES
337 The following functions are not meant to be called
338 directly, but are documented in order to explain
339 the inner workings of C<C4::Items>.
343 =head2 %derived_columns
345 This hash keeps track of item columns that
346 are strictly derived from other columns in
347 the item record and are not meant to be set
350 Each key in the hash should be the name of a
351 column (as named by TransformMarcToKoha). Each
352 value should be hashref whose keys are the
353 columns on which the derived column depends. The
354 hashref should also contain a 'BUILDER' key
355 that is a reference to a sub that calculates
360 my %derived_columns = (
362 'itemcallnumber' => 1,
363 'items.cn_source' => 1,
364 'BUILDER' => \&_calc_items_cn_sort,
368 =head2 _set_derived_columns_for_add
372 _set_derived_column_for_add($item);
376 Given an item hash representing a new item to be added,
377 calculate any derived columns. Currently the only
378 such column is C<items.cn_sort>.
382 sub _set_derived_columns_for_add {
385 foreach my $column (keys %derived_columns) {
386 my $builder = $derived_columns{$column}->{'BUILDER'};
387 my $source_values = {};
388 foreach my $source_column (keys %{ $derived_columns{$column} }) {
389 next if $source_column eq 'BUILDER';
390 $source_values->{$source_column} = $item->{$source_column};
392 $builder->($item, $source_values);
396 =head2 _set_derived_columns_for_mod
400 _set_derived_column_for_mod($item);
404 Given an item hash representing a new item to be modified.
405 calculate any derived columns. Currently the only
406 such column is C<items.cn_sort>.
408 This routine differs from C<_set_derived_columns_for_add>
409 in that it needs to handle partial item records. In other
410 words, the caller of C<ModItem> may have supplied only one
411 or two columns to be changed, so this function needs to
412 determine whether any of the columns to be changed affect
413 any of the derived columns. Also, if a derived column
414 depends on more than one column, but the caller is not
415 changing all of then, this routine retrieves the unchanged
416 values from the database in order to ensure a correct
421 sub _set_derived_columns_for_mod {
424 foreach my $column (keys %derived_columns) {
425 my $builder = $derived_columns{$column}->{'BUILDER'};
426 my $source_values = {};
427 my %missing_sources = ();
429 foreach my $source_column (keys %{ $derived_columns{$column} }) {
430 next if $source_column eq 'BUILDER';
431 if (exists $item->{$source_column}) {
433 $source_values->{$source_column} = $item->{$source_column};
435 $missing_sources{$source_column} = 1;
439 foreach my $source_column (keys %missing_sources) {
440 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
442 $builder->($item, $source_values);
447 =head2 _do_column_fixes_for_mod
451 _do_column_fixes_for_mod($item);
455 Given an item hashref containing one or more
456 columns to modify, fix up certain values.
457 Specifically, set to 0 any passed value
458 of C<notforloan>, C<damaged>, C<itemlost>, or
459 C<wthdrawn> that is either undefined or
460 contains the empty string.
464 sub _do_column_fixes_for_mod {
467 if (exists $item->{'notforloan'} and
468 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
469 $item->{'notforloan'} = 0;
471 if (exists $item->{'damaged'} and
472 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
473 $item->{'damaged'} = 0;
475 if (exists $item->{'itemlost'} and
476 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
477 $item->{'itemlost'} = 0;
479 if (exists $item->{'wthdrawn'} and
480 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
481 $item->{'wthdrawn'} = 0;
485 =head2 _get_single_item_column
489 _get_single_item_column($column, $itemnumber);
493 Retrieves the value of a single column from an C<items>
494 row specified by C<$itemnumber>.
498 sub _get_single_item_column {
500 my $itemnumber = shift;
502 my $dbh = C4::Context->dbh;
503 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
504 $sth->execute($itemnumber);
505 my ($value) = $sth->fetchrow();
509 =head2 _calc_items_cn_sort
513 _calc_items_cn_sort($item, $source_values);
517 Helper routine to calculate C<items.cn_sort>.
521 sub _calc_items_cn_sort {
523 my $source_values = shift;
525 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
528 =head2 _set_defaults_for_add
532 _set_defaults_for_add($item_hash);
536 Given an item hash representing an item to be added, set
537 correct default values for columns whose default value
538 is not handled by the DBMS. This includes the following
545 C<items.dateaccessioned>
567 sub _set_defaults_for_add {
570 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
571 if (!(exists $item->{'dateaccessioned'}) ||
572 ($item->{'dateaccessioned'} eq '')) {
573 # FIXME add check for invalid date
574 my $today = C4::Dates->new();
575 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
578 # various item status fields cannot be null
579 $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
580 $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'};
581 $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'};
582 $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'};
585 =head2 _set_calculated_values
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($record, $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( $record, $biblionumber, $itemnumber, $frameworkcode )
786 sub _replace_item_field_in_biblio {
787 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
788 my $dbh = C4::Context->dbh;
790 # get complete MARC record & replace the item field by the new one
791 my $completeRecord = GetMarcBiblio($biblionumber);
792 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
793 my $itemField = $ItemRecord->field($itemtag);
794 my @items = $completeRecord->field($itemtag);
796 if ($_->subfield($itemsubfield) eq $itemnumber) {
797 $_->replace_with($itemField);
802 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);