From c6c8e80a73178ef427fea1b66c685654a8659a91 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Thu, 3 Jan 2008 12:36:15 -0600 Subject: [PATCH] start of BIB change -- introduce C4::Items Introduced C4::Items module to separate items API from biblio API. Details on changes will be put in later commit messages. Signed-off-by: Chris Cormack Signed-off-by: Joshua Ferraro --- C4/Biblio.pm | 284 +----------------- C4/Circulation.pm | 1 + C4/ImportBatch.pm | 5 +- C4/Items.pm | 639 ++++++++++++++++++++++++++++++++++++++++ C4/Serials.pm | 3 +- acqui/finishreceive.pl | 3 +- catalogue/updateitem.pl | 27 +- cataloguing/additem.pl | 5 +- circ/returns.pl | 1 + circ/transferstodo.pl | 1 + circ/waitingreserves.pl | 1 + reports/inventory.pl | 1 + serials/serials-edit.pl | 5 +- tools/inventory.pl | 1 + 14 files changed, 668 insertions(+), 309 deletions(-) create mode 100644 C4/Items.pm diff --git a/C4/Biblio.pm b/C4/Biblio.pm index 35cb1b33c9..6edc0ee0a7 100755 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -41,7 +41,7 @@ use vars qw($VERSION @ISA @EXPORT); # EXPORTED FUNCTIONS. # to add biblios or items -push @EXPORT, qw( &AddBiblio &AddItem &AddBiblioAndItems ); +push @EXPORT, qw( &AddBiblio &AddBiblioAndItems ); # to get something push @EXPORT, qw( @@ -86,13 +86,9 @@ push @EXPORT, qw( # To modify something push @EXPORT, qw( &ModBiblio - &ModItem - &ModItemTransfer &ModBiblioframework &ModZebra &ModItemInMarc - &ModItemInMarconefield - &ModDateLastSeen ); # To delete something @@ -107,7 +103,6 @@ push @EXPORT, qw( # but don't use them unless you're a core developer ;-) push @EXPORT, qw( &ModBiblioMarc - &AddItemInMarc ); # Others functions @@ -397,75 +392,6 @@ sub _repack_item_errors { return @repacked_errors; } -=head2 AddItem - -=over 2 - - $biblionumber = AddItem( $record, $biblionumber) - Exported function (core API) for adding a new item to Koha - -=back - -=cut - -sub AddItem { - my ( $record, $biblionumber ) = @_; - my $dbh = C4::Context->dbh; - # add item in old-DB - my $frameworkcode = GetFrameworkCode( $biblionumber ); - my $item = &TransformMarcToKoha( $dbh, $record, $frameworkcode ); - - # needs old biblionumber and biblioitemnumber - $item->{'biblionumber'} = $biblionumber; - my $sth = - $dbh->prepare( - "SELECT biblioitemnumber,itemtype FROM biblioitems WHERE biblionumber=?" - ); - $sth->execute( $item->{'biblionumber'} ); - my $itemtype; - ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow; - $sth = - $dbh->prepare( - "SELECT notforloan FROM itemtypes WHERE itemtype=?"); - $sth->execute( C4::Context->preference('item-level_itypes') ? $item->{'itype'} : $itemtype ); - my $notforloan = $sth->fetchrow; - ##Change the notforloan field if $notforloan found - if ( $notforloan > 0 ) { - $item->{'notforloan'} = $notforloan; - &MARCitemchange( $record, "items.notforloan", $notforloan ); - } - if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) { - - # find today's date - my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = - localtime(time); - $year += 1900; - $mon += 1; - my $date = - "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday ); - $item->{'dateaccessioned'} = $date; - &MARCitemchange( $record, "items.dateaccessioned", $date ); - } - my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} ); - # add itemnumber to MARC::Record before adding the item. - $sth = $dbh->prepare( -"SELECT tagfield,tagsubfield -FROM marc_subfield_structure -WHERE frameworkcode=? - AND kohafield=?" - ); - &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber, - $frameworkcode ); - - # add the item - &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode ); - - &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") - if C4::Context->preference("CataloguingLog"); - - return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber); -} - =head2 ModBiblio ModBiblio( $record,$biblionumber,$frameworkcode); @@ -527,76 +453,6 @@ sub ModBiblio { return 1; } -=head2 ModItem - -=over 2 - -Exported function (core API) for modifying an item in Koha. - -=back - -=cut - -sub ModItem { - my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref ) - = @_; - - #logging - &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted) - if C4::Context->preference("CataloguingLog"); - - my $dbh = C4::Context->dbh; - - # if we have a MARC record, we're coming from cataloging and so - # we do the whole routine: update the MARC and zebra, then update the koha - # tables - if ($record) { - my $frameworkcode = GetFrameworkCode( $biblionumber ); - ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode ); - my $olditem = TransformMarcToKoha( $dbh, $record, $frameworkcode,'items'); - $olditem->{'biblionumber'} = $biblionumber; - my $sth = $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?"); - $sth->execute($biblionumber); - my ($biblioitemnumber) = $sth->fetchrow; - $sth->finish(); - $olditem->{'biblioitemnumber'} = $biblioitemnumber; - _koha_modify_item( $dbh, $olditem ); - return $biblionumber; - } - - # otherwise, we're just looking to modify something quickly - # (like a status) so we just update the koha tables - elsif ($new_item_hashref) { - _koha_modify_item( $dbh, $new_item_hashref ); - } -} - -sub ModItemTransfer { - my ( $itemnumber, $frombranch, $tobranch ) = @_; - - my $dbh = C4::Context->dbh; - - #new entry in branchtransfers.... - my $sth = $dbh->prepare( - "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch) - VALUES (?, ?, NOW(), ?)"); - $sth->execute($itemnumber, $frombranch, $tobranch); - #update holdingbranch in items ..... - $sth= $dbh->prepare( - "UPDATE items SET holdingbranch = ? WHERE items.itemnumber = ?"); - $sth->execute($tobranch,$itemnumber); - &ModDateLastSeen($itemnumber); - $sth = $dbh->prepare( - "SELECT biblionumber FROM items WHERE itemnumber=?" - ); - $sth->execute($itemnumber); - while ( my ( $biblionumber ) = $sth->fetchrow ) { - &ModItemInMarconefield( $biblionumber, $itemnumber, - 'items.holdingbranch', $tobranch ); - } - return; -} - =head2 ModBiblioframework ModBiblioframework($biblionumber,$frameworkcode); @@ -614,46 +470,6 @@ sub ModBiblioframework { return 1; } -=head2 ModItemInMarconefield - -=over - -modify only 1 field in a MARC item (mainly used for holdingbranch, but could also be used for status modif - moving a book to "lost" on a long overdu for example) -&ModItemInMarconefield( $biblionumber, $itemnumber, $itemfield, $newvalue ) - -=back - -=cut - -sub ModItemInMarconefield { - my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_; - my $dbh = C4::Context->dbh; - if ( !defined $newvalue ) { - $newvalue = ""; - } - - my $record = GetMarcItem( $biblionumber, $itemnumber ); - my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,''); - # FIXME - the condition is done this way because GetMarcFromKohaField - # returns (0, 0) if it can't field a MARC tag for the kohafield. However, - # some fields like items.wthdrawn are mapped to subfield $0, making the - # customary test of "if ($tagfield && $tagsubfield)" incorrect. - # GetMarcFromKohaField should probably be returning (undef, undef), making - # the correct test "if (defined $tagfield && defined $tagsubfield)", but - # this would be a large change and consequently deferred for after 3.0. - if (not(int($tagfield) == 0 && int($tagsubfield) == 0)) { - my $tag = $record->field($tagfield); - if ($tag) { -# my $tagsubs = $record->field($tagfield)->subfield($tagsubfield); - $tag->update( $tagsubfield => $newvalue ); - $record->delete_field($tag); - $record->insert_fields_ordered($tag); - my $frameworkcode = GetFrameworkCode( $biblionumber ); - &ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode ); - } - } -} - =head2 ModItemInMarc =over @@ -686,24 +502,6 @@ sub ModItemInMarc { ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord); } -=head2 ModDateLastSeen - -&ModDateLastSeen($itemnum) -Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking -C<$itemnum> is the item number - -=cut - -sub ModDateLastSeen { - my ($itemnum) = @_; - my $dbh = C4::Context->dbh; - my $sth = - $dbh->prepare( - "UPDATE items SET itemlost=0,datelastseen = NOW() WHERE items.itemnumber = ?" - ); - $sth->execute($itemnum); - return; -} =head2 DelBiblio =over @@ -4212,54 +4010,6 @@ sub _koha_new_items { return ( $itemnumber, $error ); } -=head2 _koha_modify_item - -=over 4 - -my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op ); - -=back - -=cut - -sub _koha_modify_item { - my ( $dbh, $item ) = @_; - my $error; - - # calculate items.cn_sort - if($item->{'itemcallnumber'}) { - # This works, even when user is setting the call number blank (in which case - # how would we get here to calculate new (blank) of items.cn_sort?). - # - # Why? Because at present the only way to update itemcallnumber is via - # additem.pl; since it uses a MARC data-entry form, TransformMarcToKoha - # already has created $item->{'items.cn_sort'} and set it to undef because the - # subfield for items.cn_sort in the framework is specified as ignored, meaning - # that it is not supplied or passed to the form. Thus, if the user has - # blanked itemcallnumber, there is already a undef value for $item->{'items.cn_sort'}. - # - # This is subtle; it is also fragile. - $item->{'items.cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, ""); - } - my $query = "UPDATE items SET "; - my @bind; - for my $key ( keys %$item ) { - $query.="$key=?,"; - push @bind, $item->{$key}; - } - $query =~ s/,$//; - $query .= " WHERE itemnumber=?"; - push @bind, $item->{'itemnumber'}; - my $sth = $dbh->prepare($query); - $sth->execute(@bind); - if ( $dbh->errstr ) { - $error.="ERROR in _koha_modify_item $query".$dbh->errstr; - warn $error; - } - $sth->finish(); - return ($item->{'itemnumber'},$error); -} - =head2 _koha_delete_biblio =over 4 @@ -4456,38 +4206,6 @@ sub ModBiblioMarc { return $biblionumber; } -=head2 AddItemInMarc - -=over 4 - -$newbiblionumber = AddItemInMarc( $record, $biblionumber, $frameworkcode ); - -Add an item in a MARC record and save the MARC record - -Function exported, but should NOT be used, unless you really know what you're doing - -=back - -=cut - -sub AddItemInMarc { - - # pass the MARC::Record to this function, and it will create the records in the marc tables - my ( $record, $biblionumber, $frameworkcode ) = @_; - my $newrec = &GetMarcBiblio($biblionumber); - - # create it - my @fields = $record->fields(); - foreach my $field (@fields) { - $newrec->append_fields($field); - } - - # FIXME: should we be making sure the biblionumbers are the same? - my $newbiblionumber = - &ModBiblioMarc( $newrec, $biblionumber, $frameworkcode ); - return $newbiblionumber; -} - =head2 z3950_extended_services z3950_extended_services($serviceType,$serviceOptions,$record); diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 15eb7d0fde..ca3c34fe34 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -25,6 +25,7 @@ use C4::Stats; use C4::Reserves; use C4::Koha; use C4::Biblio; +use C4::Items; use C4::Members; use C4::Dates; use Date::Calc qw( diff --git a/C4/ImportBatch.pm b/C4/ImportBatch.pm index 06d5d0769a..764b8bd882 100644 --- a/C4/ImportBatch.pm +++ b/C4/ImportBatch.pm @@ -21,6 +21,7 @@ use strict; use C4::Context; use C4::Koha; use C4::Biblio; +use C4::Items; require Exporter; @@ -528,7 +529,7 @@ sub BatchCommitItems { $sth->execute(); while (my $row = $sth->fetchrow_hashref()) { my $item_marc = MARC::Record->new_from_xml($row->{'marcxml'}, 'UTF-8', $row->{'encoding'}); - # FIXME - duplicate barcode check needs to become part of AddItem() + # FIXME - duplicate barcode check needs to become part of AddItemFromMarc() my $item = TransformMarcToKoha($dbh, $item_marc); my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'}); if ($duplicate_barcode) { @@ -539,7 +540,7 @@ sub BatchCommitItems { $updsth->execute(); $num_items_errored++; } else { - my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItem($item_marc, $biblionumber); + my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber); my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?"); $updsth->bind_param(1, 'imported'); $updsth->bind_param(2, $itemnumber); diff --git a/C4/Items.pm b/C4/Items.pm new file mode 100644 index 0000000000..6603b18a77 --- /dev/null +++ b/C4/Items.pm @@ -0,0 +1,639 @@ +package C4::Items; + +# Copyright 2007 LibLime, Inc. +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +use strict; + +require Exporter; + +use C4::Context; +use C4::Biblio; +use C4::Dates; +use MARC::Record; +use C4::ClassSource; +use C4::Log; + +use vars qw($VERSION @ISA @EXPORT); + +my $VERSION = 3.00; + +@ISA = qw( Exporter ); + +# function exports +@EXPORT = qw( + AddItemFromMarc + AddItem + ModItemFromMarc + ModItem + ModDateLastSeen + ModItemTransfer +); + +=head1 NAME + +C4::Items - item management functions + +=head1 DESCRIPTION + +This module contains an API for manipulating item +records in Koha, and is used by cataloguing, circulation, +acquisitions, and serials management. + +A Koha item record is stored in two places: the +items table and embedded in a MARC tag in the XML +version of the associated bib record in C. +This is done to allow the item information to be readily +indexed (e.g., by Zebra), but means that each item +modification transaction must keep the items table +and the MARC XML in sync at all times. + +Consequently, all code that creates, modifies, or deletes +item records B use an appropriate function from +C. If no existing function is suitable, it is +better to add one to C than to use add +one-off SQL statements to add or modify items. + +The items table will be considered authoritative. In other +words, if there is ever a discrepancy between the items +table and the MARC XML, the items table should be considered +accurate. + +=head1 HISTORICAL NOTE + +Most of the functions in C were originally in +the C module. + +=head1 EXPORTED FUNCTIONS + +The following functions are meant for use by users +of C + +=cut + +=head2 AddItemFromMarc + +=over 4 + +my ($biblionumber, $biblioitemnumber, $itemnumber) + = AddItemFromMarc($source_item_marc, $biblionumber); + +=back + +Given a MARC::Record object containing an embedded item +record and a biblionumber, create a new item record. + +=cut + +sub AddItemFromMarc { + my ( $source_item_marc, $biblionumber ) = @_; + my $dbh = C4::Context->dbh; + + # parse item hash from MARC + my $frameworkcode = GetFrameworkCode( $biblionumber ); + my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode ); + + return AddItem($item, $biblionumber, $dbh, $frameworkcode); +} + +=head2 AddItem + +=over 4 + +my ($biblionumber, $biblioitemnumber, $itemnumber) + = AddItem($item, $biblionumber[, $dbh, $frameworkcode]); + +=back + +Given a hash containing item column names as keys, +create a new Koha item record. + +The two optional parameters (C<$dbh> and C<$frameworkcode>) +do not need to be supplied for general use; they exist +simply to allow them to be picked up from AddItemFromMarc. + +=cut + +sub AddItem { + my $item = shift; + my $biblionumber = shift; + + my $dbh = @_ ? shift : C4::Context->dbh; + my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber ); + + # needs old biblionumber and biblioitemnumber + $item->{'biblionumber'} = $biblionumber; + my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?"); + $sth->execute( $item->{'biblionumber'} ); + ($item->{'biblioitemnumber'}) = $sth->fetchrow; + + _set_defaults_for_add($item); + _set_derived_columns_for_add($item); + # FIXME - checks here + my ( $itemnumber, $error ) = _koha_new_item( $dbh, $item, $item->{barcode} ); + $item->{'itemnumber'} = $itemnumber; + + # create MARC tag representing item and add to bib + my $new_item_marc = _marc_from_item_hash($item, $frameworkcode); + _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode ); + + logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") + if C4::Context->preference("CataloguingLog"); + + return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber); +} + +=head2 ModItemFromMarc + +=cut + +sub ModItemFromMarc { + my $item_marc = shift; + my $biblionumber = shift; + my $itemnumber = shift; + + my $dbh = C4::Context->dbh; + my $frameworkcode = GetFrameworkCode( $biblionumber ); + my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode ); + + return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode); +} + +=head2 ModItem + +=cut + +sub ModItem { + my $item = shift; + my $biblionumber = shift; + my $itemnumber = shift; + + # if $biblionumber is undefined, get it from the current item + unless (defined $biblionumber) { + $biblionumber = _get_single_item_column('biblionumber', $itemnumber); + } + + my $dbh = @_ ? shift : C4::Context->dbh; + my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber ); + + $item->{'itemnumber'} = $itemnumber; + _set_derived_columns_for_mod($item); + # FIXME add fixes + # FIXME add checks + + # update items table + _koha_modify_item($dbh, $item); + + # update biblio MARC XML + my $whole_item = GetItem($itemnumber); + my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode); + ModItemInMarc($new_item_marc, $biblionumber, $itemnumber, $frameworkcode); + + logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted) + if C4::Context->preference("CataloguingLog"); +} + +=head2 ModItemTransfer + +=cut + +sub ModItemTransfer { + my ( $itemnumber, $frombranch, $tobranch ) = @_; + + my $dbh = C4::Context->dbh; + + #new entry in branchtransfers.... + my $sth = $dbh->prepare( + "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch) + VALUES (?, ?, NOW(), ?)"); + $sth->execute($itemnumber, $frombranch, $tobranch); + + ModItem({ holdingbranch => $tobranch }, undef, $itemnumber); + ModDateLastSeen($itemnumber); + return; +} + +=head2 ModDateLastSeen + +=over 4 + +ModDateLastSeen($itemnum); + +=back + +Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking. +C<$itemnum> is the item number + +=cut + +sub ModDateLastSeen { + my ($itemnumber) = @_; + + my $today = C4::Dates->new(); + ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber); +} + +=head1 PRIVATE FUNCTIONS AND VARIABLES + +The following functions are not meant to be called +directly, but are documented in order to explain +the inner workings of C. + +=cut + +=head2 %derived_columns + +This hash keeps track of item columns that +are strictly derived from other columns in +the item record and are not meant to be set +independently. + +Each key in the hash should be the name of a +column (as named by TransformMarcToKoha). Each +value should be hashref whose keys are the +columns on which the derived column depends. The +hashref should also contain a 'BUILDER' key +that is a reference to a sub that calculates +the derived value. + +=cut + +my %derived_columns = ( + 'items.cn_sort' => { + 'itemcallnumber' => 1, + 'items.cn_source' => 1, + 'BUILDER' => \&_calc_items_cn_sort, + } +); + +=head2 _set_derived_columns_for_add + +=over 4 + +_set_derived_column_for_add($item); + +=back + +Given an item hash representing a new item to be added, +calculate any derived columns. Currently the only +such column is C. + +=cut + +sub _set_derived_columns_for_add { + my $item = shift; + + foreach my $column (keys %derived_columns) { + my $builder = $derived_columns{$column}->{'BUILDER'}; + my $source_values = {}; + foreach my $source_column (keys %{ $derived_columns{$column} }) { + next if $source_column eq 'BUILDER'; + $source_values->{$source_column} = $item->{$source_column}; + } + $builder->($item, $source_values); + } +} + +=head2 _set_derived_columns_for_mod + +=over 4 + +_set_derived_column_for_mod($item); + +=back + +Given an item hash representing a new item to be modified. +calculate any derived columns. Currently the only +such column is C. + +This routine differs from C<_set_derived_columns_for_add> +in that it needs to handle partial item records. In other +words, the caller of C may have supplied only one +or two columns to be changed, so this function needs to +determine whether any of the columns to be changed affect +any of the derived columns. Also, if a derived column +depends on more than one column, but the caller is not +changing all of then, this routine retrieves the unchanged +values from the database in order to ensure a correct +calculation. + +=cut + +sub _set_derived_columns_for_mod { + my $item = shift; + + foreach my $column (keys %derived_columns) { + my $builder = $derived_columns{$column}->{'BUILDER'}; + my $source_values = {}; + my %missing_sources = (); + my $must_recalc = 0; + foreach my $source_column (keys %{ $derived_columns{$column} }) { + next if $source_column eq 'BUILDER'; + if (exists $item->{$source_column}) { + $must_recalc = 1; + $source_values->{$source_column} = $item->{$source_column}; + } else { + $missing_sources{$source_column} = 1; + } + } + if ($must_recalc) { + foreach my $source_column (keys %missing_sources) { + $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'}); + } + $builder->($item, $source_values); + } + } +} + +sub _get_single_item_column { + my $column = shift; + my $itemnumber = shift; + + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?"); + $sth->execute($itemnumber); + my ($value) = $sth->fetchrow(); + return $value; +} + +=head2 _calc_items_cn_sort + +=over 4 + +_calc_items_cn_sort($item, $source_values); + +=back + +Helper routine to calculate C. + +=cut + +sub _calc_items_cn_sort { + my $item = shift; + my $source_values = shift; + + $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, ""); +} + +=head2 _set_defaults_for_add + +=over 4 + +_set_defaults_for_add($item_hash); + +=back + +Given an item hash representing an item to be added, set +correct default values for columns whose default value +is not handled by the DBMS. This includes the following +columns: + +=over 2 + +=item * + +C + +=item * + +C + +=item * + +C + +=item * + +C + +=item * + +C + +=back + +=cut + +sub _set_defaults_for_add { + my $item = shift; + + # if dateaccessioned is provided, use it. Otherwise, set to NOW() + if (!(exists $item->{'dateaccessioned'}) || + ($item->{'dateaccessioned'} eq '')) { + # FIXME add check for invalid date + my $today = C4::Dates->new(); + $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues + } + + # various item status fields cannot be null + $item->{'notforloan'} = 0 unless exists $item->{'notforloan'}; + $item->{'damaged'} = 0 unless exists $item->{'damaged'}; + $item->{'itemlost'} = 0 unless exists $item->{'itemlost'}; + $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'}; +} + +=head2 _set_calculated_values + +=head2 _koha_new_item + +=over 4 + +my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode ); + +=back + +=cut + +sub _koha_new_item { + my ( $dbh, $item, $barcode ) = @_; + my $error; + + my $query = + "INSERT INTO items SET + biblionumber = ?, + biblioitemnumber = ?, + barcode = ?, + dateaccessioned = ?, + booksellerid = ?, + homebranch = ?, + price = ?, + replacementprice = ?, + replacementpricedate = NOW(), + datelastborrowed = ?, + datelastseen = NOW(), + stack = ?, + notforloan = ?, + damaged = ?, + itemlost = ?, + wthdrawn = ?, + itemcallnumber = ?, + restricted = ?, + itemnotes = ?, + holdingbranch = ?, + paidfor = ?, + location = ?, + onloan = ?, + issues = ?, + renewals = ?, + reserves = ?, + cn_source = ?, + cn_sort = ?, + ccode = ?, + itype = ?, + materials = ?, + uri = ? + "; + my $sth = $dbh->prepare($query); + $sth->execute( + $item->{'biblionumber'}, + $item->{'biblioitemnumber'}, + $barcode, + $item->{'dateaccessioned'}, + $item->{'booksellerid'}, + $item->{'homebranch'}, + $item->{'price'}, + $item->{'replacementprice'}, + $item->{datelastborrowed}, + $item->{stack}, + $item->{'notforloan'}, + $item->{'damaged'}, + $item->{'itemlost'}, + $item->{'wthdrawn'}, + $item->{'itemcallnumber'}, + $item->{'restricted'}, + $item->{'itemnotes'}, + $item->{'holdingbranch'}, + $item->{'paidfor'}, + $item->{'location'}, + $item->{'onloan'}, + $item->{'issues'}, + $item->{'renewals'}, + $item->{'reserves'}, + $item->{'items.cn_source'}, + $item->{'items.cn_sort'}, + $item->{'ccode'}, + $item->{'itype'}, + $item->{'materials'}, + $item->{'uri'}, + ); + my $itemnumber = $dbh->{'mysql_insertid'}; + if ( defined $sth->errstr ) { + $error.="ERROR in _koha_new_item $query".$sth->errstr; + } + $sth->finish(); + return ( $itemnumber, $error ); +} + +=head2 _koha_modify_item + +=over 4 + +my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op ); + +=back + +=cut + +sub _koha_modify_item { + my ( $dbh, $item ) = @_; + my $error; + + my $query = "UPDATE items SET "; + my @bind; + for my $key ( keys %$item ) { + $query.="$key=?,"; + push @bind, $item->{$key}; + } + $query =~ s/,$//; + $query .= " WHERE itemnumber=?"; + push @bind, $item->{'itemnumber'}; + my $sth = $dbh->prepare($query); + $sth->execute(@bind); + if ( $dbh->errstr ) { + $error.="ERROR in _koha_modify_item $query".$dbh->errstr; + warn $error; + } + $sth->finish(); + return ($item->{'itemnumber'},$error); +} + +=head2 _marc_from_item_hash + +=over 4 + +my $item_marc = _marc_from_item_hash($item, $frameworkcode); + +=back + +Given an item hash representing a complete item record, +create a C object containing an embedded +tag representing that item. + +=cut + +sub _marc_from_item_hash { + my $item = shift; + my $frameworkcode = shift; + + # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work + # Also, don't emit a subfield if the underlying field is blank. + my $mungeditem = { map { $item->{$_} ne '' ? + (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_})) + : () } keys %{ $item } }; + + my $item_marc = MARC::Record->new(); + foreach my $item_field (keys %{ $mungeditem }) { + my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode); + next unless defined $tag and defined $subfield; # skip if not mapped to MARC field + if (my $field = $item_marc->field($tag)) { + $field->add_subfields($subfield => $mungeditem->{$item_field}); + } else { + $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field}); + } + } + + return $item_marc; +} + +=head2 _add_item_field_to_biblio + +=over 4 + +_add_item_field_to_biblio($record, $biblionumber, $frameworkcode); + +=back + +Adds the fields from a MARC record containing the +representation of a Koha item record to the MARC +biblio record. The input C<$item_marc> record +is expect to contain just one field, the embedded +item information field. + +=cut + +sub _add_item_field_to_biblio { + my ($item_marc, $biblionumber, $frameworkcode) = @_; + + my $biblio_marc = GetMarcBiblio($biblionumber); + + foreach my $field ($item_marc->fields()) { + $biblio_marc->append_fields($field); + } + + ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode); +} +1; diff --git a/C4/Serials.pm b/C4/Serials.pm index 38b5a41c1f..5673dea2f4 100644 --- a/C4/Serials.pm +++ b/C4/Serials.pm @@ -25,6 +25,7 @@ use POSIX qw(strftime); use C4::Suggestions; use C4::Koha; use C4::Biblio; +use C4::Items; use C4::Search; use C4::Letters; use C4::Log; # logaction @@ -1695,7 +1696,7 @@ sub ItemizeSerials { $marcrecord->insert_fields_ordered($newField); } } - AddItem( $marcrecord, $data->{'biblionumber'} ); + AddItemFromMarc( $marcrecord, $data->{'biblionumber'} ); return 1; } return ( 0, @errors ); diff --git a/acqui/finishreceive.pl b/acqui/finishreceive.pl index 0a70e971e2..1d5b169985 100755 --- a/acqui/finishreceive.pl +++ b/acqui/finishreceive.pl @@ -28,6 +28,7 @@ use C4::Output; use C4::Context; use C4::Acquisition; use C4::Biblio; +use C4::Items; use C4::Search; my $input=new CGI; @@ -75,7 +76,7 @@ if ($quantityrec > $origquantityrec ) { "items.itype" => $itemtype[$cnt], "items.location" => $location[$cnt], "items.loan" => 0, }); - AddItem($itemRecord,$biblionumber); + AddItemFromMarc($itemRecord,$biblionumber); } } print $input->redirect("/cgi-bin/koha/acqui/parcel.pl?invoice=$invoiceno&supplierid=$supplierid&freight=$freight&gst=$gst&datereceived=$datereceived"); diff --git a/catalogue/updateitem.pl b/catalogue/updateitem.pl index 83a11ee4fd..24b1a7e6ea 100755 --- a/catalogue/updateitem.pl +++ b/catalogue/updateitem.pl @@ -22,6 +22,7 @@ use warnings; use CGI; use C4::Context; use C4::Biblio; +use C4::Items; use C4::Output; use C4::Circulation; use C4::Accounts; @@ -51,32 +52,22 @@ for ($damaged,$itemlost,$wthdrawn) { } # modify MARC item if input differs from items table. -if ( $itemnotes ne $item_data_hashref->{'itemnotes'}) { - ModItemInMarconefield($biblionumber, $itemnumber, 'items.itemnotes', $itemnotes); - $item_data_hashref->{'itemnotes'} = $itemnotes; +my $item_changes = {}; +if (defined $itemnotes and ($itemnotes ne $item_data_hashref->{'itemnotes'})) { + $item_changes->{'itemnotes'} = $itemnotes; } elsif ($itemlost ne $item_data_hashref->{'itemlost'}) { - ModItemInMarconefield($biblionumber, $itemnumber, 'items.itemlost', $itemlost); - $item_data_hashref->{'itemlost'} = $itemlost; + $item_changes->{'itemlost'} = $itemlost; } elsif ($wthdrawn ne $item_data_hashref->{'wthdrawn'}) { - ModItemInMarconefield($biblionumber, $itemnumber, 'items.wthdrawn', $wthdrawn); - $item_data_hashref->{'wthdrawn'} = $wthdrawn; + $item_changes->{'wthdrawn'} = $wthdrawn; } elsif ($damaged ne $item_data_hashref->{'damaged'}) { - ModItemInMarconefield($biblionumber, $itemnumber, 'items.damaged', $damaged); - $item_data_hashref->{'damaged'} = $damaged; + $item_changes->{'damaged'} = $damaged; } else { #nothings changed, so do nothing. print $cgi->redirect("moredetail.pl?biblionumber=$biblionumber&itemnumber=$itemnumber"); } -# FIXME: eventually we'll use Biblio.pm, but it's currently too buggy (is this current ??) -# yes as of dec 30 2007, ModItem doesn't update zebra for status changes, it requires -# a MARC record be passed in -#ModItem( $dbh,'',$biblionumber,$itemnumber,'',$item_hashref ); -# &C4::Biblio::_koha_modify_item($dbh,$item_data_hashref); - my $sth = $dbh->prepare("UPDATE items SET wthdrawn=?,itemlost=?,damaged=?,itemnotes=? WHERE itemnumber=?"); - $sth->execute($wthdrawn,$itemlost,$damaged,$itemnotes,$itemnumber); - &ModZebra($biblionumber,"specialUpdate","biblioserver"); - +ModItem($item_changes, $biblionumber, $itemnumber); + # check issues iff itemlost. # http://wiki.koha.org/doku.php?id=en:development:kohastatuses # lost ==1 Lost, lost==2 longoverdue, lost==3 lost and paid for diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl index 88b9f4952c..e575fa6055 100755 --- a/cataloguing/additem.pl +++ b/cataloguing/additem.pl @@ -23,6 +23,7 @@ use strict; use C4::Auth; use C4::Output; use C4::Biblio; +use C4::Items; use C4::Context; use C4::Koha; # XXX subfield_is_koha_internal_p use C4::Branch; # XXX subfield_is_koha_internal_p @@ -115,7 +116,7 @@ if ($op eq "additem") { my $exist_itemnumber = get_item_from_barcode($addedolditem->{'barcode'}); push @errors,"barcode_not_unique" if($exist_itemnumber); # if barcode exists, don't create, but report The problem. - my ($oldbiblionumber,$oldbibnum,$oldbibitemnum) = AddItem($record,$biblionumber) unless ($exist_itemnumber); + my ($oldbiblionumber,$oldbibnum,$oldbibitemnum) = AddItemFromMarc($record,$biblionumber) unless ($exist_itemnumber); if ($exist_itemnumber) { $nextop = "additem"; $itemrecord = $record; @@ -174,7 +175,7 @@ if ($op eq "additem") { if ($exist_itemnumber && $exist_itemnumber != $itemnumber) { push @errors,"barcode_not_unique"; } else { - my ($oldbiblionumber,$oldbibnum,$oldbibitemnum) = ModItem($itemtosave,$biblionumber,$itemnumber,0); + my ($oldbiblionumber,$oldbibnum,$oldbibitemnum) = ModItemFromMarc($itemtosave,$biblionumber,$itemnumber); $itemnumber=""; } $nextop="additem"; diff --git a/circ/returns.pl b/circ/returns.pl index c7fcb87ea1..c97bd60ba0 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -35,6 +35,7 @@ use C4::Dates qw/format_date/; use C4::Print; use C4::Reserves; use C4::Biblio; +use C4::Items; use C4::Members; use C4::Branch; # GetBranchName use C4::Koha; # FIXME : is it still useful ? diff --git a/circ/transferstodo.pl b/circ/transferstodo.pl index 3ad348f748..18466ab16a 100755 --- a/circ/transferstodo.pl +++ b/circ/transferstodo.pl @@ -35,6 +35,7 @@ use Date::Calc qw( ); use C4::Koha; use C4::Biblio; +use C4::Items; my $input = new CGI; diff --git a/circ/waitingreserves.pl b/circ/waitingreserves.pl index 5e937f20b9..7d805b8dac 100755 --- a/circ/waitingreserves.pl +++ b/circ/waitingreserves.pl @@ -28,6 +28,7 @@ use C4::Dates qw/format_date/; use C4::Circulation; use C4::Members; use C4::Biblio; +use C4::Items; use Date::Calc qw( Today diff --git a/reports/inventory.pl b/reports/inventory.pl index eebd6348ba..aadbf28af3 100755 --- a/reports/inventory.pl +++ b/reports/inventory.pl @@ -23,6 +23,7 @@ use C4::Auth; use C4::Context; use C4::Output; use C4::Biblio; +use C4::Items; use C4::Dates qw/format_date/; # Fixed variables diff --git a/serials/serials-edit.pl b/serials/serials-edit.pl index 70e2208f8e..f5024a909b 100755 --- a/serials/serials-edit.pl +++ b/serials/serials-edit.pl @@ -67,6 +67,7 @@ use CGI; use C4::Auth; use C4::Dates qw/format_date format_date_in_iso/; use C4::Biblio; +use C4::Items; use C4::Koha; use C4::Output; use C4::Context; @@ -241,12 +242,12 @@ if ($op eq 'serialchangestatus') { $template->param("barcode_not_unique" => 1,'errserialseq'=>$serialseqs[$index]); # if barcode exists, don't create, but report The problem. unless ($exists){ - my ($biblionumber,$bibitemnum,$itemnumber) = AddItem($record,$itemhash{$item}->{'bibnum'}); + my ($biblionumber,$bibitemnum,$itemnumber) = AddItemFromMarc($record,$itemhash{$item}->{'bibnum'}); AddItem2Serial($itemhash{$item}->{'serial'},$itemnumber); } } else { #modify item - my ($oldbiblionumber,$oldbibnum,$itemnumber) = ModItem($record,$itemhash{$item}->{'bibnum'},$item,0); + my ($oldbiblionumber,$oldbibnum,$itemnumber) = ModItemFromMarc($record,$itemhash{$item}->{'bibnum'},$item); } } } diff --git a/tools/inventory.pl b/tools/inventory.pl index 6c330088a6..48d9c50538 100755 --- a/tools/inventory.pl +++ b/tools/inventory.pl @@ -23,6 +23,7 @@ use C4::Auth; use C4::Context; use C4::Output; use C4::Biblio; +use C4::Items; use C4::Dates qw/format_date format_date_in_iso/; use C4::Koha; use C4::Branch; # GetBranches -- 2.39.5