_replace_item_field_in_biblio now handles missing item field
[koha.git] / C4 / Items.pm
1 package C4::Items;
2
3 # Copyright 2007 LibLime, Inc.
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
15 #
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
19
20 use strict;
21
22 require Exporter;
23
24 use C4::Context;
25 use C4::Biblio;
26 use C4::Dates;
27 use MARC::Record;
28 use C4::ClassSource;
29 use C4::Log;
30
31 use vars qw($VERSION @ISA @EXPORT);
32
33 my $VERSION = 3.00;
34
35 @ISA = qw( Exporter );
36
37 # function exports
38 @EXPORT = qw(
39     GetItem
40     AddItemFromMarc
41     AddItem
42     ModItemFromMarc
43     ModItem
44     ModDateLastSeen
45     ModItemTransfer
46 );
47
48 =head1 NAME
49
50 C4::Items - item management functions
51
52 =head1 DESCRIPTION
53
54 This module contains an API for manipulating item 
55 records in Koha, and is used by cataloguing, circulation,
56 acquisitions, and serials management.
57
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.
65
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.
71
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.
76
77 =head1 HISTORICAL NOTE
78
79 Most of the functions in C<C4::Items> were originally in
80 the C<C4::Biblio> module.
81
82 =head1 EXPORTED FUNCTIONS
83
84 The following functions are meant for use by users
85 of C<C4::Items>
86
87 =cut
88
89 =head2 GetItem
90
91 =over 4
92
93 $item = GetItem($itemnumber,$barcode);
94
95 =back
96
97 Return item information, for a given itemnumber or barcode.
98 The return value is a hashref mapping item column
99 names to values.
100
101 =cut
102
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 = ?"
117             );
118         $sth->execute($barcode);
119         my $data = $sth->fetchrow_hashref;
120         return $data;
121     }
122 }    # sub GetItem
123
124 =head2 AddItemFromMarc
125
126 =over 4
127
128 my ($biblionumber, $biblioitemnumber, $itemnumber) 
129     = AddItemFromMarc($source_item_marc, $biblionumber);
130
131 =back
132
133 Given a MARC::Record object containing an embedded item
134 record and a biblionumber, create a new item record.
135
136 =cut
137
138 sub AddItemFromMarc {
139     my ( $source_item_marc, $biblionumber ) = @_;
140     my $dbh = C4::Context->dbh;
141
142     # parse item hash from MARC
143     my $frameworkcode = GetFrameworkCode( $biblionumber );
144     my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
145
146     return AddItem($item, $biblionumber, $dbh, $frameworkcode);
147 }
148
149 =head2 AddItem
150
151 =over 4
152
153 my ($biblionumber, $biblioitemnumber, $itemnumber) 
154     = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
155
156 =back
157
158 Given a hash containing item column names as keys,
159 create a new Koha item record.
160
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.
164
165 =cut
166
167 sub AddItem {
168     my $item = shift;
169     my $biblionumber = shift;
170
171     my $dbh           = @_ ? shift : C4::Context->dbh;
172     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
173
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;
179
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;
185
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 );
189    
190     logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
191         if C4::Context->preference("CataloguingLog");
192     
193     return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
194 }
195
196 =head2 ModItemFromMarc
197
198 =cut
199
200 sub ModItemFromMarc {
201     my $item_marc = shift;
202     my $biblionumber = shift;
203     my $itemnumber = shift;
204
205     my $dbh = C4::Context->dbh;
206     my $frameworkcode = GetFrameworkCode( $biblionumber );
207     my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
208    
209     return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode); 
210 }
211
212 =head2 ModItem
213
214 =cut
215
216 sub ModItem {
217     my $item = shift;
218     my $biblionumber = shift;
219     my $itemnumber = shift;
220
221     # if $biblionumber is undefined, get it from the current item
222     unless (defined $biblionumber) {
223         $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
224     }
225
226     my $dbh           = @_ ? shift : C4::Context->dbh;
227     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
228
229     $item->{'itemnumber'} = $itemnumber;
230     _set_derived_columns_for_mod($item);
231     _do_column_fixes_for_mod($item);
232     # FIXME add checks
233
234     # update items table
235     _koha_modify_item($dbh, $item);
236
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);
241     
242     logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
243         if C4::Context->preference("CataloguingLog");
244 }
245
246 =head2 ModItemTransfer
247
248 =cut
249
250 sub ModItemTransfer {
251     my ( $itemnumber, $frombranch, $tobranch ) = @_;
252
253     my $dbh = C4::Context->dbh;
254
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);
260
261     ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
262     ModDateLastSeen($itemnumber);
263     return;
264 }
265
266 =head2 ModDateLastSeen
267
268 =over 4
269
270 ModDateLastSeen($itemnum);
271
272 =back
273
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
276
277 =cut
278
279 sub ModDateLastSeen {
280     my ($itemnumber) = @_;
281     
282     my $today = C4::Dates->new();    
283     ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
284 }
285
286 =head1 LIMITED USE FUNCTIONS
287
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.
293
294 =cut
295
296 =head2 GetMarcItem
297
298 =over 4
299
300 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
301
302 =back
303
304 Returns MARC::Record of the item passed in parameter.
305 This function is meant for use only in C<cataloguing/additem.pl>,
306 where it is needed to support that script's MARC-like
307 editor.
308
309 =cut
310
311 sub GetMarcItem {
312     my ( $biblionumber, $itemnumber ) = @_;
313
314     # GetMarcItem has been revised so that it does the following:
315     #  1. Gets the item information from the items table.
316     #  2. Converts it to a MARC field for storage in the bib record.
317     #
318     # The previous behavior was:
319     #  1. Get the bib record.
320     #  2. Return the MARC tag corresponding to the item record.
321     #
322     # The difference is that one treats the items row as authoritative,
323     # while the other treats the MARC representation as authoritative
324     # under certain circumstances.
325
326     my $itemrecord = GetItem($itemnumber);
327
328     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
329     # Also, don't emit a subfield if the underlying field is blank.
330     my $mungeditem = { map {  $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  } keys %{ $itemrecord } };
331
332     my $itemmarc = TransformKohaToMarc($mungeditem);
333     return $itemmarc;
334
335 }
336
337 =head1 PRIVATE FUNCTIONS AND VARIABLES
338
339 The following functions are not meant to be called
340 directly, but are documented in order to explain
341 the inner workings of C<C4::Items>.
342
343 =cut
344
345 =head2 %derived_columns
346
347 This hash keeps track of item columns that
348 are strictly derived from other columns in
349 the item record and are not meant to be set
350 independently.
351
352 Each key in the hash should be the name of a
353 column (as named by TransformMarcToKoha).  Each
354 value should be hashref whose keys are the
355 columns on which the derived column depends.  The
356 hashref should also contain a 'BUILDER' key
357 that is a reference to a sub that calculates
358 the derived value.
359
360 =cut
361
362 my %derived_columns = (
363     'items.cn_sort' => {
364         'itemcallnumber' => 1,
365         'items.cn_source' => 1,
366         'BUILDER' => \&_calc_items_cn_sort,
367     }
368 );
369
370 =head2 _set_derived_columns_for_add 
371
372 =over 4
373
374 _set_derived_column_for_add($item);
375
376 =back
377
378 Given an item hash representing a new item to be added,
379 calculate any derived columns.  Currently the only
380 such column is C<items.cn_sort>.
381
382 =cut
383
384 sub _set_derived_columns_for_add {
385     my $item = shift;
386
387     foreach my $column (keys %derived_columns) {
388         my $builder = $derived_columns{$column}->{'BUILDER'};
389         my $source_values = {};
390         foreach my $source_column (keys %{ $derived_columns{$column} }) {
391             next if $source_column eq 'BUILDER';
392             $source_values->{$source_column} = $item->{$source_column};
393         }
394         $builder->($item, $source_values);
395     }
396 }
397
398 =head2 _set_derived_columns_for_mod 
399
400 =over 4
401
402 _set_derived_column_for_mod($item);
403
404 =back
405
406 Given an item hash representing a new item to be modified.
407 calculate any derived columns.  Currently the only
408 such column is C<items.cn_sort>.
409
410 This routine differs from C<_set_derived_columns_for_add>
411 in that it needs to handle partial item records.  In other
412 words, the caller of C<ModItem> may have supplied only one
413 or two columns to be changed, so this function needs to
414 determine whether any of the columns to be changed affect
415 any of the derived columns.  Also, if a derived column
416 depends on more than one column, but the caller is not
417 changing all of then, this routine retrieves the unchanged
418 values from the database in order to ensure a correct
419 calculation.
420
421 =cut
422
423 sub _set_derived_columns_for_mod {
424     my $item = shift;
425
426     foreach my $column (keys %derived_columns) {
427         my $builder = $derived_columns{$column}->{'BUILDER'};
428         my $source_values = {};
429         my %missing_sources = ();
430         my $must_recalc = 0;
431         foreach my $source_column (keys %{ $derived_columns{$column} }) {
432             next if $source_column eq 'BUILDER';
433             if (exists $item->{$source_column}) {
434                 $must_recalc = 1;
435                 $source_values->{$source_column} = $item->{$source_column};
436             } else {
437                 $missing_sources{$source_column} = 1;
438             }
439         }
440         if ($must_recalc) {
441             foreach my $source_column (keys %missing_sources) {
442                 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
443             }
444             $builder->($item, $source_values);
445         }
446     }
447 }
448
449 =head2 _do_column_fixes_for_mod
450
451 =over 4
452
453 _do_column_fixes_for_mod($item);
454
455 =back
456
457 Given an item hashref containing one or more
458 columns to modify, fix up certain values.
459 Specifically, set to 0 any passed value
460 of C<notforloan>, C<damaged>, C<itemlost>, or
461 C<wthdrawn> that is either undefined or
462 contains the empty string.
463
464 =cut
465
466 sub _do_column_fixes_for_mod {
467     my $item = shift;
468
469     if (exists $item->{'notforloan'} and
470         (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
471         $item->{'notforloan'} = 0;
472     }
473     if (exists $item->{'damaged'} and
474         (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
475         $item->{'damaged'} = 0;
476     }
477     if (exists $item->{'itemlost'} and
478         (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
479         $item->{'itemlost'} = 0;
480     }
481     if (exists $item->{'wthdrawn'} and
482         (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
483         $item->{'wthdrawn'} = 0;
484     }
485 }
486
487 =head2 _get_single_item_column
488
489 =over 4
490
491 _get_single_item_column($column, $itemnumber);
492
493 =back
494
495 Retrieves the value of a single column from an C<items>
496 row specified by C<$itemnumber>.
497
498 =cut
499
500 sub _get_single_item_column {
501     my $column = shift;
502     my $itemnumber = shift;
503     
504     my $dbh = C4::Context->dbh;
505     my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
506     $sth->execute($itemnumber);
507     my ($value) = $sth->fetchrow();
508     return $value; 
509 }
510
511 =head2 _calc_items_cn_sort
512
513 =over 4
514
515 _calc_items_cn_sort($item, $source_values);
516
517 =back
518
519 Helper routine to calculate C<items.cn_sort>.
520
521 =cut
522
523 sub _calc_items_cn_sort {
524     my $item = shift;
525     my $source_values = shift;
526
527     $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
528 }
529
530 =head2 _set_defaults_for_add 
531
532 =over 4
533
534 _set_defaults_for_add($item_hash);
535
536 =back
537
538 Given an item hash representing an item to be added, set
539 correct default values for columns whose default value
540 is not handled by the DBMS.  This includes the following
541 columns:
542
543 =over 2
544
545 =item * 
546
547 C<items.dateaccessioned>
548
549 =item *
550
551 C<items.notforloan>
552
553 =item *
554
555 C<items.damaged>
556
557 =item *
558
559 C<items.itemlost>
560
561 =item *
562
563 C<items.wthdrawn>
564
565 =back
566
567 =cut
568
569 sub _set_defaults_for_add {
570     my $item = shift;
571
572     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
573     if (!(exists $item->{'dateaccessioned'}) || 
574          ($item->{'dateaccessioned'} eq '')) {
575         # FIXME add check for invalid date
576         my $today = C4::Dates->new();    
577         $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
578     }
579
580     # various item status fields cannot be null
581     $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
582     $item->{'damaged'}    = 0 unless exists $item->{'damaged'}    and defined $item->{'damaged'};
583     $item->{'itemlost'}   = 0 unless exists $item->{'itemlost'}   and defined $item->{'itemlost'};
584     $item->{'wthdrawn'}   = 0 unless exists $item->{'wthdrawn'}   and defined $item->{'wthdrawn'};
585 }
586
587 =head2 _koha_new_item
588
589 =over 4
590
591 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
592
593 =back
594
595 =cut
596
597 sub _koha_new_item {
598     my ( $dbh, $item, $barcode ) = @_;
599     my $error;
600
601     my $query = 
602            "INSERT INTO items SET
603             biblionumber        = ?,
604             biblioitemnumber    = ?,
605             barcode             = ?,
606             dateaccessioned     = ?,
607             booksellerid        = ?,
608             homebranch          = ?,
609             price               = ?,
610             replacementprice    = ?,
611             replacementpricedate = NOW(),
612             datelastborrowed    = ?,
613             datelastseen        = NOW(),
614             stack               = ?,
615             notforloan          = ?,
616             damaged             = ?,
617             itemlost            = ?,
618             wthdrawn            = ?,
619             itemcallnumber      = ?,
620             restricted          = ?,
621             itemnotes           = ?,
622             holdingbranch       = ?,
623             paidfor             = ?,
624             location            = ?,
625             onloan              = ?,
626             issues              = ?,
627             renewals            = ?,
628             reserves            = ?,
629             cn_source           = ?,
630             cn_sort             = ?,
631             ccode               = ?,
632             itype               = ?,
633             materials           = ?,
634             uri                 = ?
635           ";
636     my $sth = $dbh->prepare($query);
637     $sth->execute(
638             $item->{'biblionumber'},
639             $item->{'biblioitemnumber'},
640             $barcode,
641             $item->{'dateaccessioned'},
642             $item->{'booksellerid'},
643             $item->{'homebranch'},
644             $item->{'price'},
645             $item->{'replacementprice'},
646             $item->{datelastborrowed},
647             $item->{stack},
648             $item->{'notforloan'},
649             $item->{'damaged'},
650             $item->{'itemlost'},
651             $item->{'wthdrawn'},
652             $item->{'itemcallnumber'},
653             $item->{'restricted'},
654             $item->{'itemnotes'},
655             $item->{'holdingbranch'},
656             $item->{'paidfor'},
657             $item->{'location'},
658             $item->{'onloan'},
659             $item->{'issues'},
660             $item->{'renewals'},
661             $item->{'reserves'},
662             $item->{'items.cn_source'},
663             $item->{'items.cn_sort'},
664             $item->{'ccode'},
665             $item->{'itype'},
666             $item->{'materials'},
667             $item->{'uri'},
668     );
669     my $itemnumber = $dbh->{'mysql_insertid'};
670     if ( defined $sth->errstr ) {
671         $error.="ERROR in _koha_new_item $query".$sth->errstr;
672     }
673     $sth->finish();
674     return ( $itemnumber, $error );
675 }
676
677 =head2 _koha_modify_item
678
679 =over 4
680
681 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
682
683 =back
684
685 =cut
686
687 sub _koha_modify_item {
688     my ( $dbh, $item ) = @_;
689     my $error;
690
691     my $query = "UPDATE items SET ";
692     my @bind;
693     for my $key ( keys %$item ) {
694         $query.="$key=?,";
695         push @bind, $item->{$key};
696     }
697     $query =~ s/,$//;
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;
704         warn $error;
705     }
706     $sth->finish();
707     return ($item->{'itemnumber'},$error);
708 }
709
710 =head2 _marc_from_item_hash
711
712 =over 4
713
714 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
715
716 =back
717
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.
721
722 =cut
723
724 sub _marc_from_item_hash {
725     my $item = shift;
726     my $frameworkcode = shift;
727    
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 } }; 
733
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});
740         } else {
741             $item_marc->add_fields( $tag, " ", " ", $subfield =>  $mungeditem->{$item_field});
742         }
743     }
744
745     return $item_marc;
746 }
747
748 =head2 _add_item_field_to_biblio
749
750 =over 4
751
752 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
753
754 =back
755
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.
761
762 =cut
763
764 sub _add_item_field_to_biblio {
765     my ($item_marc, $biblionumber, $frameworkcode) = @_;
766
767     my $biblio_marc = GetMarcBiblio($biblionumber);
768
769     foreach my $field ($item_marc->fields()) {
770         $biblio_marc->append_fields($field);
771     }
772
773     ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
774 }
775
776 =head2 _replace_item_field_in_biblio
777
778 =over
779
780 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
781
782 =back
783
784 Given a MARC::Record C<$item_marc> containing one tag with the MARC 
785 representation of the item, examine the biblio MARC
786 for the corresponding tag for that item and 
787 replace it with the tag from C<$item_marc>.
788
789 =cut
790
791 sub _replace_item_field_in_biblio {
792     my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
793     my $dbh = C4::Context->dbh;
794     
795     # get complete MARC record & replace the item field by the new one
796     my $completeRecord = GetMarcBiblio($biblionumber);
797     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
798     my $itemField = $ItemRecord->field($itemtag);
799     my @items = $completeRecord->field($itemtag);
800     my $found = 0;
801     foreach (@items) {
802         if ($_->subfield($itemsubfield) eq $itemnumber) {
803             $_->replace_with($itemField);
804             $found = 1;
805         }
806     }
807   
808     unless ($found) { 
809         # If we haven't found the matching field,
810         # just add it.  However, this means that
811         # there is likely a bug.
812         $completeRecord->append_fields($itemField);
813     }
814
815     # save the record
816     ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
817 }
818
819 1;