check for undefined values when mapping item to MARC
[koha.git] / C4 / Items.pm
blobe7f8e315b600d87bcd42846df3735ccf09a4b695
1 package C4::Items;
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
10 # version.
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
20 use strict;
22 require Exporter;
24 use C4::Context;
25 use C4::Biblio;
26 use C4::Dates;
27 use MARC::Record;
28 use C4::ClassSource;
29 use C4::Log;
31 use vars qw($VERSION @ISA @EXPORT);
33 my $VERSION = 3.00;
35 @ISA = qw( Exporter );
37 # function exports
38 @EXPORT = qw(
39 GetItem
40 AddItemFromMarc
41 AddItem
42 ModItemFromMarc
43 ModItem
44 ModDateLastSeen
45 ModItemTransfer
48 =head1 NAME
50 C4::Items - item management functions
52 =head1 DESCRIPTION
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
75 accurate.
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
85 of C<C4::Items>
87 =cut
89 =head2 GetItem
91 =over 4
93 $item = GetItem($itemnumber,$barcode);
95 =back
97 Return item information, for a given itemnumber or barcode.
98 The return value is a hashref mapping item column
99 names to values.
101 =cut
103 sub GetItem {
104 my ($itemnumber,$barcode) = @_;
105 my $dbh = C4::Context->dbh;
106 if ($itemnumber) {
107 my $sth = $dbh->prepare("
108 SELECT * FROM items
109 WHERE itemnumber = ?");
110 $sth->execute($itemnumber);
111 my $data = $sth->fetchrow_hashref;
112 return $data;
113 } else {
114 my $sth = $dbh->prepare("
115 SELECT * FROM items
116 WHERE barcode = ?"
118 $sth->execute($barcode);
119 my $data = $sth->fetchrow_hashref;
120 return $data;
122 } # sub GetItem
124 =head2 AddItemFromMarc
126 =over 4
128 my ($biblionumber, $biblioitemnumber, $itemnumber)
129 = AddItemFromMarc($source_item_marc, $biblionumber);
131 =back
133 Given a MARC::Record object containing an embedded item
134 record and a biblionumber, create a new item record.
136 =cut
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);
149 =head2 AddItem
151 =over 4
153 my ($biblionumber, $biblioitemnumber, $itemnumber)
154 = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
156 =back
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.
165 =cut
167 sub AddItem {
168 my $item = shift;
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
198 =over 4
200 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
202 =back
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.
209 =cut
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);
223 =head2 ModItem
225 =over 4
227 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
229 =back
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
241 and set the value.
243 =cut
245 sub ModItem {
246 my $item = shift;
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);
261 # FIXME add checks
262 # duplicate barcode
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)
268 # update items table
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
282 =over 4
284 ModItemTransfer($itenumber, $frombranch, $tobranch);
286 =back
288 Marks an item as being transferred from one branch
289 to another.
291 =cut
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);
306 return;
309 =head2 ModDateLastSeen
311 =over 4
313 ModDateLastSeen($itemnum);
315 =back
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
320 =cut
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.
337 =cut
339 =head2 GetMarcItem
341 =over 4
343 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
345 =back
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
350 editor.
352 =cut
354 sub GetMarcItem {
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);
376 return $itemmarc;
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>.
386 =cut
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
393 independently.
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
401 the derived value.
403 =cut
405 my %derived_columns = (
406 'items.cn_sort' => {
407 'itemcallnumber' => 1,
408 'items.cn_source' => 1,
409 'BUILDER' => \&_calc_items_cn_sort,
413 =head2 _set_derived_columns_for_add
415 =over 4
417 _set_derived_column_for_add($item);
419 =back
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>.
425 =cut
427 sub _set_derived_columns_for_add {
428 my $item = shift;
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
443 =over 4
445 _set_derived_column_for_mod($item);
447 =back
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
462 calculation.
464 =cut
466 sub _set_derived_columns_for_mod {
467 my $item = shift;
469 foreach my $column (keys %derived_columns) {
470 my $builder = $derived_columns{$column}->{'BUILDER'};
471 my $source_values = {};
472 my %missing_sources = ();
473 my $must_recalc = 0;
474 foreach my $source_column (keys %{ $derived_columns{$column} }) {
475 next if $source_column eq 'BUILDER';
476 if (exists $item->{$source_column}) {
477 $must_recalc = 1;
478 $source_values->{$source_column} = $item->{$source_column};
479 } else {
480 $missing_sources{$source_column} = 1;
483 if ($must_recalc) {
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
494 =over 4
496 _do_column_fixes_for_mod($item);
498 =back
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.
507 =cut
509 sub _do_column_fixes_for_mod {
510 my $item = shift;
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
532 =over 4
534 _get_single_item_column($column, $itemnumber);
536 =back
538 Retrieves the value of a single column from an C<items>
539 row specified by C<$itemnumber>.
541 =cut
543 sub _get_single_item_column {
544 my $column = shift;
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();
551 return $value;
554 =head2 _calc_items_cn_sort
556 =over 4
558 _calc_items_cn_sort($item, $source_values);
560 =back
562 Helper routine to calculate C<items.cn_sort>.
564 =cut
566 sub _calc_items_cn_sort {
567 my $item = shift;
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
575 =over 4
577 _set_defaults_for_add($item_hash);
579 =back
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
584 columns:
586 =over 2
588 =item *
590 C<items.dateaccessioned>
592 =item *
594 C<items.notforloan>
596 =item *
598 C<items.damaged>
600 =item *
602 C<items.itemlost>
604 =item *
606 C<items.wthdrawn>
608 =back
610 =cut
612 sub _set_defaults_for_add {
613 my $item = shift;
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
632 =over 4
634 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
636 =back
638 Perform the actual insert into the C<items> table.
640 =cut
642 sub _koha_new_item {
643 my ( $dbh, $item, $barcode ) = @_;
644 my $error;
646 my $query =
647 "INSERT INTO items SET
648 biblionumber = ?,
649 biblioitemnumber = ?,
650 barcode = ?,
651 dateaccessioned = ?,
652 booksellerid = ?,
653 homebranch = ?,
654 price = ?,
655 replacementprice = ?,
656 replacementpricedate = NOW(),
657 datelastborrowed = ?,
658 datelastseen = NOW(),
659 stack = ?,
660 notforloan = ?,
661 damaged = ?,
662 itemlost = ?,
663 wthdrawn = ?,
664 itemcallnumber = ?,
665 restricted = ?,
666 itemnotes = ?,
667 holdingbranch = ?,
668 paidfor = ?,
669 location = ?,
670 onloan = ?,
671 issues = ?,
672 renewals = ?,
673 reserves = ?,
674 cn_source = ?,
675 cn_sort = ?,
676 ccode = ?,
677 itype = ?,
678 materials = ?,
679 uri = ?
681 my $sth = $dbh->prepare($query);
682 $sth->execute(
683 $item->{'biblionumber'},
684 $item->{'biblioitemnumber'},
685 $barcode,
686 $item->{'dateaccessioned'},
687 $item->{'booksellerid'},
688 $item->{'homebranch'},
689 $item->{'price'},
690 $item->{'replacementprice'},
691 $item->{datelastborrowed},
692 $item->{stack},
693 $item->{'notforloan'},
694 $item->{'damaged'},
695 $item->{'itemlost'},
696 $item->{'wthdrawn'},
697 $item->{'itemcallnumber'},
698 $item->{'restricted'},
699 $item->{'itemnotes'},
700 $item->{'holdingbranch'},
701 $item->{'paidfor'},
702 $item->{'location'},
703 $item->{'onloan'},
704 $item->{'issues'},
705 $item->{'renewals'},
706 $item->{'reserves'},
707 $item->{'items.cn_source'},
708 $item->{'items.cn_sort'},
709 $item->{'ccode'},
710 $item->{'itype'},
711 $item->{'materials'},
712 $item->{'uri'},
714 my $itemnumber = $dbh->{'mysql_insertid'};
715 if ( defined $sth->errstr ) {
716 $error.="ERROR in _koha_new_item $query".$sth->errstr;
718 $sth->finish();
719 return ( $itemnumber, $error );
722 =head2 _koha_modify_item
724 =over 4
726 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
728 =back
730 Perform the actual update of the C<items> row. Note that this
731 routine accepts a hashref specifying the columns to update.
733 =cut
735 sub _koha_modify_item {
736 my ( $dbh, $item ) = @_;
737 my $error;
739 my $query = "UPDATE items SET ";
740 my @bind;
741 for my $key ( keys %$item ) {
742 $query.="$key=?,";
743 push @bind, $item->{$key};
745 $query =~ s/,$//;
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;
752 warn $error;
754 $sth->finish();
755 return ($item->{'itemnumber'},$error);
758 =head2 _marc_from_item_hash
760 =over 4
762 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
764 =back
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.
770 =cut
772 sub _marc_from_item_hash {
773 my $item = shift;
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});
788 } else {
789 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field});
793 return $item_marc;
796 =head2 _add_item_field_to_biblio
798 =over 4
800 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
802 =back
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.
810 =cut
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
826 =over
828 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
830 =back
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>.
837 =cut
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);
848 my $found = 0;
849 foreach (@items) {
850 if ($_->subfield($itemsubfield) eq $itemnumber) {
851 $_->replace_with($itemField);
852 $found = 1;
856 unless ($found) {
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);
863 # save the record
864 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);