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 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
204 This function updates an item record based on a supplied
205 C<MARC::Record> object containing an embedded item field.
206 This API is meant for the use of C<additem.pl>; for
207 other purposes, C<ModItem> should be used.
211 sub ModItemFromMarc
{
212 my $item_marc = shift;
213 my $biblionumber = shift;
214 my $itemnumber = shift;
216 my $dbh = C4
::Context
->dbh;
217 my $frameworkcode = GetFrameworkCode
( $biblionumber );
218 my $item = &TransformMarcToKoha
( $dbh, $item_marc, $frameworkcode );
220 return ModItem
($item, $biblionumber, $itemnumber, $dbh, $frameworkcode);
227 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
231 Change one or more columns in an item record and update
232 the MARC representation of the item.
234 The first argument is a hashref mapping from item column
235 names to the new values. The second and third arguments
236 are the biblionumber and itemnumber, respectively.
238 If one of the changed columns is used to calculate
239 the derived value of a column such as C<items.cn_sort>,
240 this routine will perform the necessary calculation
247 my $biblionumber = shift;
248 my $itemnumber = shift;
250 # if $biblionumber is undefined, get it from the current item
251 unless (defined $biblionumber) {
252 $biblionumber = _get_single_item_column
('biblionumber', $itemnumber);
255 my $dbh = @_ ?
shift : C4
::Context
->dbh;
256 my $frameworkcode = @_ ?
shift : GetFrameworkCode
( $biblionumber );
258 $item->{'itemnumber'} = $itemnumber;
259 _set_derived_columns_for_mod
($item);
260 _do_column_fixes_for_mod
($item);
263 # attempt to change itemnumber
264 # attempt to change biblionumber (if we want
265 # an API to relink an item to a different bib,
266 # it should be a separate function)
269 _koha_modify_item
($dbh, $item);
271 # update biblio MARC XML
272 my $whole_item = GetItem
($itemnumber);
273 my $new_item_marc = _marc_from_item_hash
($whole_item, $frameworkcode);
274 _replace_item_field_in_biblio
($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
276 logaction
(C4
::Context
->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
277 if C4
::Context
->preference("CataloguingLog");
280 =head2 ModItemTransfer
284 ModItemTransfer($itenumber, $frombranch, $tobranch);
288 Marks an item as being transferred from one branch
293 sub ModItemTransfer
{
294 my ( $itemnumber, $frombranch, $tobranch ) = @_;
296 my $dbh = C4
::Context
->dbh;
298 #new entry in branchtransfers....
299 my $sth = $dbh->prepare(
300 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
301 VALUES (?, ?, NOW(), ?)");
302 $sth->execute($itemnumber, $frombranch, $tobranch);
304 ModItem
({ holdingbranch
=> $tobranch }, undef, $itemnumber);
305 ModDateLastSeen
($itemnumber);
309 =head2 ModDateLastSeen
313 ModDateLastSeen($itemnum);
317 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
318 C<$itemnum> is the item number
322 sub ModDateLastSeen
{
323 my ($itemnumber) = @_;
325 my $today = C4
::Dates
->new();
326 ModItem
({ itemlost
=> 0, datelastseen
=> $today->output("iso") }, undef, $itemnumber);
329 =head1 LIMITED USE FUNCTIONS
331 The following functions, while part of the public API,
332 are not exported. This is generally because they are
333 meant to be used by only one script for a specific
334 purpose, and should not be used in any other context
335 without careful thought.
343 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
347 Returns MARC::Record of the item passed in parameter.
348 This function is meant for use only in C<cataloguing/additem.pl>,
349 where it is needed to support that script's MARC-like
355 my ( $biblionumber, $itemnumber ) = @_;
357 # GetMarcItem has been revised so that it does the following:
358 # 1. Gets the item information from the items table.
359 # 2. Converts it to a MARC field for storage in the bib record.
361 # The previous behavior was:
362 # 1. Get the bib record.
363 # 2. Return the MARC tag corresponding to the item record.
365 # The difference is that one treats the items row as authoritative,
366 # while the other treats the MARC representation as authoritative
367 # under certain circumstances.
369 my $itemrecord = GetItem
($itemnumber);
371 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
372 # Also, don't emit a subfield if the underlying field is blank.
373 my $mungeditem = { map { $itemrecord->{$_} ne '' ?
("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
375 my $itemmarc = TransformKohaToMarc
($mungeditem);
380 =head1 PRIVATE FUNCTIONS AND VARIABLES
382 The following functions are not meant to be called
383 directly, but are documented in order to explain
384 the inner workings of C<C4::Items>.
388 =head2 %derived_columns
390 This hash keeps track of item columns that
391 are strictly derived from other columns in
392 the item record and are not meant to be set
395 Each key in the hash should be the name of a
396 column (as named by TransformMarcToKoha). Each
397 value should be hashref whose keys are the
398 columns on which the derived column depends. The
399 hashref should also contain a 'BUILDER' key
400 that is a reference to a sub that calculates
405 my %derived_columns = (
407 'itemcallnumber' => 1,
408 'items.cn_source' => 1,
409 'BUILDER' => \
&_calc_items_cn_sort
,
413 =head2 _set_derived_columns_for_add
417 _set_derived_column_for_add($item);
421 Given an item hash representing a new item to be added,
422 calculate any derived columns. Currently the only
423 such column is C<items.cn_sort>.
427 sub _set_derived_columns_for_add
{
430 foreach my $column (keys %derived_columns) {
431 my $builder = $derived_columns{$column}->{'BUILDER'};
432 my $source_values = {};
433 foreach my $source_column (keys %{ $derived_columns{$column} }) {
434 next if $source_column eq 'BUILDER';
435 $source_values->{$source_column} = $item->{$source_column};
437 $builder->($item, $source_values);
441 =head2 _set_derived_columns_for_mod
445 _set_derived_column_for_mod($item);
449 Given an item hash representing a new item to be modified.
450 calculate any derived columns. Currently the only
451 such column is C<items.cn_sort>.
453 This routine differs from C<_set_derived_columns_for_add>
454 in that it needs to handle partial item records. In other
455 words, the caller of C<ModItem> may have supplied only one
456 or two columns to be changed, so this function needs to
457 determine whether any of the columns to be changed affect
458 any of the derived columns. Also, if a derived column
459 depends on more than one column, but the caller is not
460 changing all of then, this routine retrieves the unchanged
461 values from the database in order to ensure a correct
466 sub _set_derived_columns_for_mod
{
469 foreach my $column (keys %derived_columns) {
470 my $builder = $derived_columns{$column}->{'BUILDER'};
471 my $source_values = {};
472 my %missing_sources = ();
474 foreach my $source_column (keys %{ $derived_columns{$column} }) {
475 next if $source_column eq 'BUILDER';
476 if (exists $item->{$source_column}) {
478 $source_values->{$source_column} = $item->{$source_column};
480 $missing_sources{$source_column} = 1;
484 foreach my $source_column (keys %missing_sources) {
485 $source_values->{$source_column} = _get_single_item_column
($source_column, $item->{'itemnumber'});
487 $builder->($item, $source_values);
492 =head2 _do_column_fixes_for_mod
496 _do_column_fixes_for_mod($item);
500 Given an item hashref containing one or more
501 columns to modify, fix up certain values.
502 Specifically, set to 0 any passed value
503 of C<notforloan>, C<damaged>, C<itemlost>, or
504 C<wthdrawn> that is either undefined or
505 contains the empty string.
509 sub _do_column_fixes_for_mod
{
512 if (exists $item->{'notforloan'} and
513 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
514 $item->{'notforloan'} = 0;
516 if (exists $item->{'damaged'} and
517 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
518 $item->{'damaged'} = 0;
520 if (exists $item->{'itemlost'} and
521 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
522 $item->{'itemlost'} = 0;
524 if (exists $item->{'wthdrawn'} and
525 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
526 $item->{'wthdrawn'} = 0;
530 =head2 _get_single_item_column
534 _get_single_item_column($column, $itemnumber);
538 Retrieves the value of a single column from an C<items>
539 row specified by C<$itemnumber>.
543 sub _get_single_item_column
{
545 my $itemnumber = shift;
547 my $dbh = C4
::Context
->dbh;
548 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
549 $sth->execute($itemnumber);
550 my ($value) = $sth->fetchrow();
554 =head2 _calc_items_cn_sort
558 _calc_items_cn_sort($item, $source_values);
562 Helper routine to calculate C<items.cn_sort>.
566 sub _calc_items_cn_sort
{
568 my $source_values = shift;
570 $item->{'items.cn_sort'} = GetClassSort
($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
573 =head2 _set_defaults_for_add
577 _set_defaults_for_add($item_hash);
581 Given an item hash representing an item to be added, set
582 correct default values for columns whose default value
583 is not handled by the DBMS. This includes the following
590 C<items.dateaccessioned>
612 sub _set_defaults_for_add
{
615 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
616 if (!(exists $item->{'dateaccessioned'}) ||
617 ($item->{'dateaccessioned'} eq '')) {
618 # FIXME add check for invalid date
619 my $today = C4
::Dates
->new();
620 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
623 # various item status fields cannot be null
624 $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
625 $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'};
626 $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'};
627 $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'};
630 =head2 _koha_new_item
634 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
638 Perform the actual insert into the C<items> table.
643 my ( $dbh, $item, $barcode ) = @_;
647 "INSERT INTO items SET
649 biblioitemnumber = ?,
655 replacementprice = ?,
656 replacementpricedate = NOW(),
657 datelastborrowed = ?,
658 datelastseen = NOW(),
681 my $sth = $dbh->prepare($query);
683 $item->{'biblionumber'},
684 $item->{'biblioitemnumber'},
686 $item->{'dateaccessioned'},
687 $item->{'booksellerid'},
688 $item->{'homebranch'},
690 $item->{'replacementprice'},
691 $item->{datelastborrowed
},
693 $item->{'notforloan'},
697 $item->{'itemcallnumber'},
698 $item->{'restricted'},
699 $item->{'itemnotes'},
700 $item->{'holdingbranch'},
707 $item->{'items.cn_source'},
708 $item->{'items.cn_sort'},
711 $item->{'materials'},
714 my $itemnumber = $dbh->{'mysql_insertid'};
715 if ( defined $sth->errstr ) {
716 $error.="ERROR in _koha_new_item $query".$sth->errstr;
719 return ( $itemnumber, $error );
722 =head2 _koha_modify_item
726 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
730 Perform the actual update of the C<items> row. Note that this
731 routine accepts a hashref specifying the columns to update.
735 sub _koha_modify_item
{
736 my ( $dbh, $item ) = @_;
739 my $query = "UPDATE items SET ";
741 for my $key ( keys %$item ) {
743 push @bind, $item->{$key};
746 $query .= " WHERE itemnumber=?";
747 push @bind, $item->{'itemnumber'};
748 my $sth = $dbh->prepare($query);
749 $sth->execute(@bind);
750 if ( $dbh->errstr ) {
751 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
755 return ($item->{'itemnumber'},$error);
758 =head2 _marc_from_item_hash
762 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
766 Given an item hash representing a complete item record,
767 create a C<MARC::Record> object containing an embedded
768 tag representing that item.
772 sub _marc_from_item_hash
{
774 my $frameworkcode = shift;
776 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
777 # Also, don't emit a subfield if the underlying field is blank.
778 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
779 (/^items\./ ?
($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
780 : () } keys %{ $item } };
782 my $item_marc = MARC
::Record
->new();
783 foreach my $item_field (keys %{ $mungeditem }) {
784 my ($tag, $subfield) = GetMarcFromKohaField
($item_field, $frameworkcode);
785 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
786 if (my $field = $item_marc->field($tag)) {
787 $field->add_subfields($subfield => $mungeditem->{$item_field});
789 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field});
796 =head2 _add_item_field_to_biblio
800 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
804 Adds the fields from a MARC record containing the
805 representation of a Koha item record to the MARC
806 biblio record. The input C<$item_marc> record
807 is expect to contain just one field, the embedded
808 item information field.
812 sub _add_item_field_to_biblio
{
813 my ($item_marc, $biblionumber, $frameworkcode) = @_;
815 my $biblio_marc = GetMarcBiblio
($biblionumber);
817 foreach my $field ($item_marc->fields()) {
818 $biblio_marc->append_fields($field);
821 ModBiblioMarc
($biblio_marc, $biblionumber, $frameworkcode);
824 =head2 _replace_item_field_in_biblio
828 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
832 Given a MARC::Record C<$item_marc> containing one tag with the MARC
833 representation of the item, examine the biblio MARC
834 for the corresponding tag for that item and
835 replace it with the tag from C<$item_marc>.
839 sub _replace_item_field_in_biblio
{
840 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
841 my $dbh = C4
::Context
->dbh;
843 # get complete MARC record & replace the item field by the new one
844 my $completeRecord = GetMarcBiblio
($biblionumber);
845 my ($itemtag,$itemsubfield) = GetMarcFromKohaField
("items.itemnumber",$frameworkcode);
846 my $itemField = $ItemRecord->field($itemtag);
847 my @items = $completeRecord->field($itemtag);
850 if ($_->subfield($itemsubfield) eq $itemnumber) {
851 $_->replace_with($itemField);
857 # If we haven't found the matching field,
858 # just add it. However, this means that
859 # there is likely a bug.
860 $completeRecord->append_fields($itemField);
864 ModBiblioMarc
($completeRecord, $biblionumber, $frameworkcode);