[3.0.x] (bug #4263) fix the edition of items with repeatable subfields
[koha.git] / C4 / Items.pm
index bc5a56a..d19caf9 100644 (file)
@@ -28,6 +28,7 @@ use C4::ClassSource;
 use C4::Log;
 use C4::Branch;
 require C4::Reserves;
 use C4::Log;
 use C4::Branch;
 require C4::Reserves;
+use C4::Charset;
 
 use vars qw($VERSION @ISA @EXPORT);
 
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -48,6 +49,7 @@ BEGIN {
         ModDateLastSeen
         ModItemTransfer
         DelItem
         ModDateLastSeen
         ModItemTransfer
         DelItem
+        DelItemCheck
     
         CheckItemPreSave
     
     
         CheckItemPreSave
     
@@ -142,7 +144,13 @@ sub GetItem {
         $ssth->execute($data->{'itemnumber'}) ;
         ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
                warn $data->{'serialseq'} , $data->{'publisheddate'};
         $ssth->execute($data->{'itemnumber'}) ;
         ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
                warn $data->{'serialseq'} , $data->{'publisheddate'};
-    }          
+    }
+       #if we don't have an items.itype, use biblioitems.itemtype.
+       if( ! $data->{'itype'} ) {
+               my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems  WHERE biblionumber = ?");
+               $sth->execute($data->{'biblionumber'});
+               ($data->{'itype'}) = $sth->fetchrow_array;
+       }
     return $data;
 }    # sub GetItem
 
     return $data;
 }    # sub GetItem
 
@@ -166,8 +174,13 @@ sub AddItemFromMarc {
 
     # parse item hash from MARC
     my $frameworkcode = GetFrameworkCode( $biblionumber );
 
     # parse item hash from MARC
     my $frameworkcode = GetFrameworkCode( $biblionumber );
-    my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
-    return AddItem($item, $biblionumber, $dbh, $frameworkcode,$source_item_marc);
+       my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
+       
+       my $localitemmarc=MARC::Record->new;
+       $localitemmarc->append_fields($source_item_marc->field($itemtag));
+    my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
+    my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
+    return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
 }
 
 =head2 AddItem
 }
 
 =head2 AddItem
@@ -175,17 +188,24 @@ sub AddItemFromMarc {
 =over 4
 
 my ($biblionumber, $biblioitemnumber, $itemnumber) 
 =over 4
 
 my ($biblionumber, $biblioitemnumber, $itemnumber) 
-    = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
+    = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
 
 =back
 
 Given a hash containing item column names as keys,
 create a new Koha item record.
 
 
 =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>)
+The first 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.
 
 do not need to be supplied for general use; they exist
 simply to allow them to be picked up from AddItemFromMarc.
 
+The final optional parameter, C<$unlinked_item_subfields>, contains
+an arrayref containing subfields present in the original MARC
+representation of the item (e.g., from the item editor) that are
+not mapped to C<items> columns directly but should instead
+be stored in C<items.more_subfields_xml> and included in 
+the biblio items tag for display and indexing.
+
 =cut
 
 sub AddItem {
 =cut
 
 sub AddItem {
@@ -194,8 +214,10 @@ sub AddItem {
 
     my $dbh           = @_ ? shift : C4::Context->dbh;
     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
 
     my $dbh           = @_ ? shift : C4::Context->dbh;
     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
-    my $item_marc_record;  
-    if (@_){$item_marc_record=shift};
+    my $unlinked_item_subfields;  
+    if (@_) {
+        $unlinked_item_subfields = shift
+    };
 
     # needs old biblionumber and biblioitemnumber
     $item->{'biblionumber'} = $biblionumber;
 
     # needs old biblionumber and biblioitemnumber
     $item->{'biblionumber'} = $biblionumber;
@@ -205,17 +227,22 @@ sub AddItem {
 
     _set_defaults_for_add($item);
     _set_derived_columns_for_add($item);
 
     _set_defaults_for_add($item);
     _set_derived_columns_for_add($item);
+    $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
     # FIXME - checks here
     # FIXME - checks here
-         my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
+    unless ( $item->{itype} ) {  # default to biblioitem.itemtype if no itype
+        my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
+        $itype_sth->execute( $item->{'biblionumber'} );
+        ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
+    }
+
+       my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
     $item->{'itemnumber'} = $itemnumber;
 
     # create MARC tag representing item and add to bib
     $item->{'itemnumber'} = $itemnumber;
 
     # create MARC tag representing item and add to bib
-    my $new_item_marc = _marc_from_item_hash($item, $frameworkcode);
-    if ($item_marc_record){_add_unlinked_marc_fields($new_item_marc,$item_marc_record,$frameworkcode);}
+    my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
     _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $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");
+    logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
     
     return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
 }
     
     return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
 }
@@ -290,6 +317,8 @@ sub AddItemBatchFromMarc {
     
         # add biblionumber and biblioitemnumber
         my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
     
         # add biblionumber and biblioitemnumber
         my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
+        my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
+        $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
         $item->{'biblionumber'} = $biblionumber;
         $item->{'biblioitemnumber'} = $biblioitemnumber;
 
         $item->{'biblionumber'} = $biblionumber;
         $item->{'biblioitemnumber'} = $biblioitemnumber;
 
@@ -308,10 +337,9 @@ sub AddItemBatchFromMarc {
         push @itemnumbers, $itemnumber; # FIXME not checking error
         $item->{'itemnumber'} = $itemnumber;
 
         push @itemnumbers, $itemnumber; # FIXME not checking error
         $item->{'itemnumber'} = $itemnumber;
 
-        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
-        if C4::Context->preference("CataloguingLog"); 
+        logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog"); 
 
 
-        my $new_item_marc = _marc_from_item_hash($item, $frameworkcode);
+        my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
         $item_field->replace_with($new_item_marc->field($itemtag));
     }
 
         $item_field->replace_with($new_item_marc->field($itemtag));
     }
 
@@ -339,8 +367,49 @@ C<MARC::Record> object containing an embedded item field.
 This API is meant for the use of C<additem.pl>; for 
 other purposes, C<ModItem> should be used.
 
 This API is meant for the use of C<additem.pl>; for 
 other purposes, C<ModItem> should be used.
 
+This function uses the hash %default_values_for_mod_from_marc,
+which contains default values for item fields to
+apply when modifying an item.  This is needed beccause
+if an item field's value is cleared, TransformMarcToKoha
+does not include the column in the
+hash that's passed to ModItem, which without
+use of this hash makes it impossible to clear
+an item field's value.  See bug 2466.
+
+Note that only columns that can be directly
+changed from the cataloging and serials
+item editors are included in this hash.
+
 =cut
 
 =cut
 
+my %default_values_for_mod_from_marc = (
+    barcode              => undef, 
+    booksellerid         => undef, 
+    ccode                => undef, 
+    'items.cn_source'    => undef, 
+    copynumber           => undef, 
+    damaged              => 0,
+    dateaccessioned      => undef, 
+    enumchron            => undef, 
+    holdingbranch        => undef, 
+    homebranch           => undef, 
+    itemcallnumber       => undef, 
+    itemlost             => 0,
+    itemnotes            => undef, 
+    itype                => undef, 
+    location             => undef, 
+    materials            => undef, 
+    notforloan           => 0,
+    paidfor              => undef, 
+    price                => undef, 
+    replacementprice     => undef, 
+    replacementpricedate => undef, 
+    restricted           => undef, 
+    stack                => undef, 
+    uri                  => undef, 
+    wthdrawn             => 0,
+);
+
 sub ModItemFromMarc {
     my $item_marc = shift;
     my $biblionumber = shift;
 sub ModItemFromMarc {
     my $item_marc = shift;
     my $biblionumber = shift;
@@ -348,16 +417,24 @@ sub ModItemFromMarc {
 
     my $dbh = C4::Context->dbh;
     my $frameworkcode = GetFrameworkCode( $biblionumber );
 
     my $dbh = C4::Context->dbh;
     my $frameworkcode = GetFrameworkCode( $biblionumber );
-    my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
-   
-    return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $item_marc); 
+       my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
+       
+       my $localitemmarc=MARC::Record->new;
+       $localitemmarc->append_fields($item_marc->field($itemtag));
+    my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items');
+    foreach my $item_field (keys %default_values_for_mod_from_marc) {
+        $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
+    }
+    my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
+
+    return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields); 
 }
 
 =head2 ModItem
 
 =over 4
 
 }
 
 =head2 ModItem
 
 =over 4
 
-ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
+ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]);
 
 =back
 
 
 =back
 
@@ -368,6 +445,13 @@ The first argument is a hashref mapping from item column
 names to the new values.  The second and third arguments
 are the biblionumber and itemnumber, respectively.
 
 names to the new values.  The second and third arguments
 are the biblionumber and itemnumber, respectively.
 
+The fourth, optional parameter, C<$unlinked_item_subfields>, contains
+an arrayref containing subfields present in the original MARC
+representation of the item (e.g., from the item editor) that are
+not mapped to C<items> columns directly but should instead
+be stored in C<items.more_subfields_xml> and included in 
+the biblio items tag for display and indexing.
+
 If one of the changed columns is used to calculate
 the derived value of a column such as C<items.cn_sort>, 
 this routine will perform the necessary calculation
 If one of the changed columns is used to calculate
 the derived value of a column such as C<items.cn_sort>, 
 this routine will perform the necessary calculation
@@ -388,8 +472,11 @@ sub ModItem {
     my $dbh           = @_ ? shift : C4::Context->dbh;
     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
     
     my $dbh           = @_ ? shift : C4::Context->dbh;
     my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
     
-    my $item_marc_record;  
-    if (@_){$item_marc_record=shift};
+    my $unlinked_item_subfields;  
+    if (@_) {
+        $unlinked_item_subfields = shift;
+        $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
+    };
 
     $item->{'itemnumber'} = $itemnumber or return undef;
     _set_derived_columns_for_mod($item);
 
     $item->{'itemnumber'} = $itemnumber or return undef;
     _set_derived_columns_for_mod($item);
@@ -406,17 +493,16 @@ sub ModItem {
 
     # update biblio MARC XML
     my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
 
     # update biblio MARC XML
     my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
-    my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode) or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
+
+    unless (defined $unlinked_item_subfields) {
+        $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
+    }
+    my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields) 
+        or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
     
     
-    if ($item_marc_record){
-      _add_unlinked_marc_fields($new_item_marc,$item_marc_record,$frameworkcode);
-    }   
-                  
     _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
     _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
-       (C4::Context->userenv eq '0') and die "userenv is '0', not hashref";         # logaction line would crash anyway
        ($new_item_marc       eq '0') and die "$new_item_marc is '0', not hashref";  # logaction line would crash anyway
        ($new_item_marc       eq '0') and die "$new_item_marc is '0', not hashref";  # logaction line would crash anyway
-    logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
-        if C4::Context->preference("CataloguingLog");
+    logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
 }
 
 =head2 ModItemTransfer
 }
 
 =head2 ModItemTransfer
@@ -506,8 +592,48 @@ sub DelItem {
         }
     }
     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
         }
     }
     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
-    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item") 
-        if C4::Context->preference("CataloguingLog");
+    logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
+}
+
+=head2 DelItemCheck
+
+=over 4
+
+DelItemCheck($dbh, $biblionumber, $itemnumber);
+
+=back
+
+Exported function (core API) for deleting an item record in Koha if there no current issue.
+
+=cut
+
+sub DelItemCheck {
+    my ( $dbh, $biblionumber, $itemnumber ) = @_;
+    my $error;
+
+    # check that there is no issue on this item before deletion.
+    my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
+    $sth->execute($itemnumber);
+
+    my $onloan=$sth->fetchrow;
+
+    $sth->finish();
+    if ($onloan){
+        $error = "book_on_loan" 
+    }else{
+        # check it doesnt have a waiting reserve
+        $sth=$dbh->prepare("SELECT * FROM reserves WHERE found = 'W' AND itemnumber = ?");
+        $sth->execute($itemnumber);
+        my $reserve=$sth->fetchrow;
+        $sth->finish();
+        if ($reserve){
+            $error = "book_reserved";
+        }else{
+            DelItem($dbh, $biblionumber, $itemnumber);
+            return 1;
+        }
+    }
+    return $error;
 }
 
 =head2 CheckItemPreSave
 }
 
 =head2 CheckItemPreSave
@@ -815,28 +941,35 @@ sub GetItemLocation {
 
 =over 4
 
 
 =over 4
 
-$items = GetLostItems($where,$orderby);
+$items = GetLostItems( $where, $orderby );
 
 =back
 
 
 =back
 
-This function get the items lost into C<$items>.
+This function gets a list of lost items.
 
 =over 2
 
 =item input:
 
 =over 2
 
 =item input:
+
 C<$where> is a hashref. it containts a field of the items table as key
 C<$where> is a hashref. it containts a field of the items table as key
-and the value to match as value.
-C<$orderby> is a field of the items table.
+and the value to match as value. For example:
+
+{ barcode    => 'abc123',
+  homebranch => 'CPL',    }
+
+C<$orderby> is a field of the items table by which the resultset
+should be orderd.
 
 =item return:
 
 =item return:
-C<$items> is a reference to an array full of hasref which keys are items' table column.
+
+C<$items> is a reference to an array full of hashrefs with columns
+from the "items" table as keys.
 
 =item usage in the perl script:
 
 
 =item usage in the perl script:
 
-my %where;
-$where{barcode} = 0001548;
-my $items = GetLostItems( \%where, "homebranch" );
-$template->param(itemsloop => $items);
+my $where = { barcode => '0001548' };
+my $items = GetLostItems( $where, "homebranch" );
+$template->param( itemsloop => $items );
 
 =back
 
 
 =back
 
@@ -851,75 +984,116 @@ sub GetLostItems {
     my $query   = "
         SELECT *
         FROM   items
     my $query   = "
         SELECT *
         FROM   items
-        WHERE  itemlost IS NOT NULL
-          AND  itemlost <> 0
+            LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
+            LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
+            LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
+        WHERE
+               authorised_values.category = 'LOST'
+               AND itemlost IS NOT NULL
+               AND itemlost <> 0
     ";
     ";
+    my @query_parameters;
     foreach my $key (keys %$where) {
     foreach my $key (keys %$where) {
-        $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
+        $query .= " AND $key LIKE ?";
+        push @query_parameters, "%$where->{$key}%";
+    }
+    my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
+    
+    if ( defined $orderby && grep($orderby, @ordervalues)) {
+        $query .= ' ORDER BY '.$orderby;
     }
     }
-    $query .= " ORDER BY ".$orderby if defined $orderby;
 
     my $sth = $dbh->prepare($query);
 
     my $sth = $dbh->prepare($query);
-    $sth->execute;
-    my @items;
+    $sth->execute( @query_parameters );
+    my $items = [];
     while ( my $row = $sth->fetchrow_hashref ){
     while ( my $row = $sth->fetchrow_hashref ){
-        push @items, $row;
+        push @$items, $row;
     }
     }
-    return \@items;
+    return $items;
 }
 
 =head2 GetItemsForInventory
 
 =over 4
 
 }
 
 =head2 GetItemsForInventory
 
 =over 4
 
-$itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
+$itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size);
 
 =back
 
 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
 
 
 =back
 
 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
 
-The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
-It is ordered by callnumber,title.
+The sub returns a reference to a list of hashes, each containing
+itemnumber, author, title, barcode, item callnumber, and date last
+seen. It is ordered by callnumber then title.
 
 
-The minlocation & maxlocation parameters are used to specify a range of item callnumbers
+The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
 the datelastseen can be used to specify that you want to see items not seen since a past date only.
 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
 
 =cut
 
 sub GetItemsForInventory {
 the datelastseen can be used to specify that you want to see items not seen since a past date only.
 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
 
 =cut
 
 sub GetItemsForInventory {
-    my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
+    my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branch, $offset, $size ) = @_;
     my $dbh = C4::Context->dbh;
     my $dbh = C4::Context->dbh;
-    my $sth;
+    my ( @bind_params, @where_strings );
+
+    my $query = <<'END_SQL';
+SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
+FROM items
+  LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
+  LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
+END_SQL
+
+    if ($minlocation) {
+        push @where_strings, 'itemcallnumber >= ?';
+        push @bind_params, $minlocation;
+    }
+
+    if ($maxlocation) {
+        push @where_strings, 'itemcallnumber <= ?';
+        push @bind_params, $maxlocation;
+    }
+
     if ($datelastseen) {
     if ($datelastseen) {
-        $datelastseen=format_date_in_iso($datelastseen);  
-        my $query =
-                "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
-                 FROM items
-                   LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
-                 WHERE itemcallnumber>= ?
-                   AND itemcallnumber <=?
-                   AND (datelastseen< ? OR datelastseen IS NULL)";
-        $query.= " AND items.location=".$dbh->quote($location) if $location;
-        $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
-        $query .= " ORDER BY itemcallnumber,title";
-        $sth = $dbh->prepare($query);
-        $sth->execute( $minlocation, $maxlocation, $datelastseen );
+        $datelastseen = format_date_in_iso($datelastseen);  
+        push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
+        push @bind_params, $datelastseen;
+    }
+
+    if ( $location ) {
+        push @where_strings, 'items.location = ?';
+        push @bind_params, $location;
+    }
+    
+    if ( $branch ) {
+        push @where_strings, 'items.homebranch = ?';
+        push @bind_params, $branch;
+    }
+    
+    if ( $itemtype ) {
+        push @where_strings, 'biblioitems.itemtype = ?';
+        push @bind_params, $itemtype;
     }
     }
-    else {
-        my $query ="
-                SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
-                FROM items 
-                  LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber 
-                WHERE itemcallnumber>= ?
-                  AND itemcallnumber <=?";
-        $query.= " AND items.location=".$dbh->quote($location) if $location;
-        $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
-        $query .= " ORDER BY itemcallnumber,title";
-        $sth = $dbh->prepare($query);
-        $sth->execute( $minlocation, $maxlocation );
+    if ( $ignoreissued) {
+        $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
+        push @where_strings, 'issues.date_due IS NULL';
     }
     }
+
+    if ( $ignoreissued) {
+        $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
+        push @where_strings, 'issues.date_due IS NULL';
+    }
+
+    if ( @where_strings ) {
+        $query .= 'WHERE ';
+        $query .= join ' AND ', @where_strings;
+    }
+    $query .= ' ORDER BY itemcallnumber, title';
+    my $sth = $dbh->prepare($query);
+    $sth->execute( @bind_params );
+
     my @results;
     my @results;
+    $size--;
     while ( my $row = $sth->fetchrow_hashref ) {
         $offset-- if ($offset);
         $row->{datelastseen}=format_date($row->{datelastseen});
     while ( my $row = $sth->fetchrow_hashref ) {
         $offset-- if ($offset);
         $row->{datelastseen}=format_date($row->{datelastseen});
@@ -1001,7 +1175,6 @@ sub GetItemsByBiblioitemnumber {
         # Foreach item, get circulation information
         my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
                                    WHERE itemnumber = ?
         # Foreach item, get circulation information
         my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
                                    WHERE itemnumber = ?
-                                   AND returndate is NULL
                                    AND issues.borrowernumber = borrowers.borrowernumber"
         );
         $sth2->execute( $data->{'itemnumber'} );
                                    AND issues.borrowernumber = borrowers.borrowernumber"
         );
         $sth2->execute( $data->{'itemnumber'} );
@@ -1017,9 +1190,8 @@ sub GetItemsByBiblioitemnumber {
         }    # else         
         $sth2->finish;
         # Find the last 3 people who borrowed this item.                  
         }    # else         
         $sth2->finish;
         # Find the last 3 people who borrowed this item.                  
-        my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
-                      AND issues.borrowernumber = borrowers.borrowernumber
-                      AND returndate is not NULL
+        my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
+                      AND old_issues.borrowernumber = borrowers.borrowernumber
                       ORDER BY returndate desc,timestamp desc LIMIT 3";
         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
                       ORDER BY returndate desc,timestamp desc LIMIT 3";
         $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
         $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
@@ -1090,29 +1262,43 @@ If this is set, it is set to C<One Order>.
 sub GetItemsInfo {
     my ( $biblionumber, $type ) = @_;
     my $dbh   = C4::Context->dbh;
 sub GetItemsInfo {
     my ( $biblionumber, $type ) = @_;
     my $dbh   = C4::Context->dbh;
-    my $query = "SELECT *,items.notforloan as itemnotforloan
-                 FROM items 
-                 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-                 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
-    $query .=  (C4::Context->preference('item-level_itypes')) ?
-                     " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
-                    : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
-    $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
+    # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
+    my $query = "
+    SELECT items.*,
+           biblio.*,
+           biblioitems.volume,
+           biblioitems.number,
+           biblioitems.itemtype,
+           biblioitems.isbn,
+           biblioitems.issn,
+           biblioitems.publicationyear,
+           biblioitems.publishercode,
+           biblioitems.volumedate,
+           biblioitems.volumedesc,
+           biblioitems.lccn,
+           biblioitems.url,
+           items.notforloan as itemnotforloan,
+           itemtypes.description
+     FROM items
+     LEFT JOIN biblio      ON      biblio.biblionumber     = items.biblionumber
+     LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+     LEFT JOIN itemtypes   ON   itemtypes.itemtype         = "
+     . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
+    $query .= " WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $i = 0;
     my @results;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $i = 0;
     my @results;
-    my ( $date_due, $count_reserves, $serial );
+    my $serial;
 
     my $isth    = $dbh->prepare(
         "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
         FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
 
     my $isth    = $dbh->prepare(
         "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
         FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
-        WHERE  itemnumber = ?
-            AND returndate IS NULL"
+        WHERE  itemnumber = ?"
        );
        my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); 
        while ( my $data = $sth->fetchrow_hashref ) {
        );
        my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); 
        while ( my $data = $sth->fetchrow_hashref ) {
-          warn $data->{itemnumber};
+           my $count_reserves;
         my $datedue = '';
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
         my $datedue = '';
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
@@ -1131,7 +1317,6 @@ sub GetItemsInfo {
                if ( $data->{'serial'}) {       
                        $ssth->execute($data->{'itemnumber'}) ;
                        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
                if ( $data->{'serial'}) {       
                        $ssth->execute($data->{'itemnumber'}) ;
                        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
-                       warn $data->{'serialseq'} , $data->{'publisheddate'};
                        $serial = 1;
         }
                if ( $datedue eq '' ) {
                        $serial = 1;
         }
                if ( $datedue eq '' ) {
@@ -1176,6 +1361,7 @@ sub GetItemsInfo {
             my ($lib) = $sthnflstatus->fetchrow;
             $data->{notforloanvalue} = $lib;
         }
             my ($lib) = $sthnflstatus->fetchrow;
             $data->{notforloanvalue} = $lib;
         }
+               $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes'));
 
         # my stack procedures
         my $stackstatus = $dbh->prepare(
 
         # my stack procedures
         my $stackstatus = $dbh->prepare(
@@ -1200,10 +1386,11 @@ sub GetItemsInfo {
             $data->{stack} = $lib;
         }
         # Find the last 3 people who borrowed this item.
             $data->{stack} = $lib;
         }
         # Find the last 3 people who borrowed this item.
-        my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
+        my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
                                     WHERE itemnumber = ?
                                     WHERE itemnumber = ?
-                                    AND issues.borrowernumber = borrowers.borrowernumber
-                                    AND returndate IS NOT NULL LIMIT 3");
+                                    AND old_issues.borrowernumber = borrowers.borrowernumber
+                                    ORDER BY returndate DESC
+                                    LIMIT 3");
         $sth2->execute($data->{'itemnumber'});
         my $ii = 0;
         while (my $data2 = $sth2->fetchrow_hashref()) {
         $sth2->execute($data->{'itemnumber'});
         my $ii = 0;
         while (my $data2 = $sth2->fetchrow_hashref()) {
@@ -1216,9 +1403,8 @@ sub GetItemsInfo {
         $results[$i] = $data;
         $i++;
     }
         $results[$i] = $data;
         $i++;
     }
-    $sth->finish;
        if($serial) {
        if($serial) {
-               return( sort { $b->{'publisheddate'} cmp $a->{'publisheddate'} } @results );
+               return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
        } else {
        return (@results);
        }
        } else {
        return (@results);
        }
@@ -1284,6 +1470,106 @@ sub GetItemnumberFromBarcode {
     return ($result);
 }
 
     return ($result);
 }
 
+=head3 get_item_authorised_values
+
+  find the types and values for all authorised values assigned to this item.
+
+  parameters:
+    itemnumber
+
+  returns: a hashref malling the authorised value to the value set for this itemnumber
+
+    $authorised_values = {
+             'CCODE'      => undef,
+             'DAMAGED'    => '0',
+             'LOC'        => '3',
+             'LOST'       => '0'
+             'NOT_LOAN'   => '0',
+             'RESTRICTED' => undef,
+             'STACK'      => undef,
+             'WITHDRAWN'  => '0',
+             'branches'   => 'CPL',
+             'cn_source'  => undef,
+             'itemtypes'  => 'SER',
+           };
+
+   Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
+
+=cut
+
+sub get_item_authorised_values {
+    my $itemnumber = shift;
+
+    # assume that these entries in the authorised_value table are item level.
+    my $query = q(SELECT distinct authorised_value, kohafield
+                    FROM marc_subfield_structure
+                    WHERE kohafield like 'item%'
+                      AND authorised_value != '' );
+
+    my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
+    my $iteminfo = GetItem( $itemnumber );
+    # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
+    my $return;
+    foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
+        my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
+        $field =~ s/^items\.//;
+        if ( exists $iteminfo->{ $field } ) {
+            $return->{ $this_authorised_value } = $iteminfo->{ $field };
+        }
+    }
+    # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
+    return $return;
+}
+
+=head3 get_authorised_value_images
+
+  find a list of icons that are appropriate for display based on the
+  authorised values for a biblio.
+
+  parameters: listref of authorised values, such as comes from
+    get_item_authorised_values or
+    from C4::Biblio::get_biblio_authorised_values
+
+  returns: listref of hashrefs for each image. Each hashref looks like
+    this:
+
+      { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
+        label    => '',
+        category => '',
+        value    => '', }
+
+  Notes: Currently, I put on the full path to the images on the staff
+  side. This should either be configurable or not done at all. Since I
+  have to deal with 'intranet' or 'opac' in
+  get_biblio_authorised_values, perhaps I should be passing it in.
+
+=cut
+
+sub get_authorised_value_images {
+    my $authorised_values = shift;
+
+    my @imagelist;
+
+    my $authorised_value_list = GetAuthorisedValues();
+    # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
+    foreach my $this_authorised_value ( @$authorised_value_list ) {
+        if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
+             && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
+            # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
+            if ( defined $this_authorised_value->{'imageurl'} ) {
+                push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
+                                   label    => $this_authorised_value->{'lib'},
+                                   category => $this_authorised_value->{'category'},
+                                   value    => $this_authorised_value->{'authorised_value'}, };
+            }
+        }
+    }
+
+    # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
+    return \@imagelist;
+
+}
+
 =head1 LIMITED USE FUNCTIONS
 
 The following functions, while part of the public API,
 =head1 LIMITED USE FUNCTIONS
 
 The following functions, while part of the public API,
@@ -1328,19 +1614,22 @@ sub GetMarcItem {
 
     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
     # Also, don't emit a subfield if the underlying field is blank.
 
     # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
     # Also, don't emit a subfield if the underlying field is blank.
-    my $mungeditem = { map {  $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  } keys %{ $itemrecord } };
+    my $mungeditem = { 
+        map {  
+            defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()  
+        } keys %{ $itemrecord } 
+    };
+
     my $itemmarc = TransformKohaToMarc($mungeditem);
     my $itemmarc = TransformKohaToMarc($mungeditem);
+
+    my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
+    if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
+        my @fields = $itemmarc->fields();
+        if ($#fields > -1) {
+            $fields[0]->add_subfields(@$unlinked_item_subfields);
+        }
+    }
     
     
-    my $marc = GetMarcBiblio($biblionumber);
-    my $frameworkcode=GetFrameworkCode($biblionumber);
-    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
-    foreach my $field ($marc->field($itemtag)){
-        if ($field->subfield($itemsubfield)==$itemnumber){
-            my $tmpmarc=MARC::Record->new();
-            $tmpmarc->append_fields($field);
-            _add_unlinked_marc_fields($itemmarc,$tmpmarc,$frameworkcode);
-        }    
-    }  
     return $itemmarc;
 
 }
     return $itemmarc;
 
 }
@@ -1579,20 +1868,8 @@ C<items.wthdrawn>
 
 sub _set_defaults_for_add {
     my $item = shift;
 
 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'} and defined $item->{'notforloan'} and $item->{'notforloan'} ne '';
-    $item->{'damaged'}    = 0 unless exists $item->{'damaged'}    and defined $item->{'damaged'}    and $item->{'damaged'} ne '';
-    $item->{'itemlost'}   = 0 unless exists $item->{'itemlost'}   and defined $item->{'itemlost'}   and $item->{'itemlost'} ne '';
-    $item->{'wthdrawn'}   = 0 unless exists $item->{'wthdrawn'}   and defined $item->{'wthdrawn'}   and $item->{'wthdrawn'} ne '';
+    $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
+    $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
 }
 
 =head2 _koha_new_item
 }
 
 =head2 _koha_new_item
@@ -1644,7 +1921,10 @@ sub _koha_new_item {
             ccode               = ?,
             itype               = ?,
             materials           = ?,
             ccode               = ?,
             itype               = ?,
             materials           = ?,
-                       uri                 = ?
+            uri = ?,
+            enumchron           = ?,
+            more_subfields_xml  = ?,
+            copynumber          = ?
           ";
     my $sth = $dbh->prepare($query);
    $sth->execute(
           ";
     my $sth = $dbh->prepare($query);
    $sth->execute(
@@ -1678,6 +1958,9 @@ sub _koha_new_item {
             $item->{'itype'},
             $item->{'materials'},
             $item->{'uri'},
             $item->{'itype'},
             $item->{'materials'},
             $item->{'uri'},
+            $item->{'enumchron'},
+            $item->{'more_subfields_xml'},
+            $item->{'copynumber'},
     );
     my $itemnumber = $dbh->{'mysql_insertid'};
     if ( defined $sth->errstr ) {
     );
     my $itemnumber = $dbh->{'mysql_insertid'};
     if ( defined $sth->errstr ) {
@@ -1766,7 +2049,7 @@ sub _koha_delete_item {
 
 =over 4
 
 
 =over 4
 
-my $item_marc = _marc_from_item_hash($item, $frameworkcode);
+my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
 
 =back
 
 
 =back
 
@@ -1774,11 +2057,20 @@ Given an item hash representing a complete item record,
 create a C<MARC::Record> object containing an embedded
 tag representing that item.
 
 create a C<MARC::Record> object containing an embedded
 tag representing that item.
 
+The third, optional parameter C<$unlinked_item_subfields> is
+an arrayref of subfields (not mapped to C<items> fields per the
+framework) to be added to the MARC representation
+of the item.
+
 =cut
 
 sub _marc_from_item_hash {
     my $item = shift;
     my $frameworkcode = shift;
 =cut
 
 sub _marc_from_item_hash {
     my $item = shift;
     my $frameworkcode = shift;
+    my $unlinked_item_subfields;
+    if (@_) {
+        $unlinked_item_subfields = 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.
    
     # 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.
@@ -1790,10 +2082,19 @@ sub _marc_from_item_hash {
     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
     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});
+
+        
+        my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
+        foreach my $value (@values){
+            if (my $field = $item_marc->field($tag)) {
+                $field->add_subfields($subfield => $value);
+            } else {
+                my $add_subfields = [];
+                if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
+                    $add_subfields = $unlinked_item_subfields;
+                }
+                $item_marc->add_fields( $tag, " ", " ", $subfield =>  $value, @$add_subfields);
+            }
         }
     }
 
         }
     }
 
@@ -1820,7 +2121,6 @@ sub _add_item_field_to_biblio {
     my ($item_marc, $biblionumber, $frameworkcode) = @_;
 
     my $biblio_marc = GetMarcBiblio($biblionumber);
     my ($item_marc, $biblionumber, $frameworkcode) = @_;
 
     my $biblio_marc = GetMarcBiblio($biblionumber);
-
     foreach my $field ($item_marc->fields()) {
         $biblio_marc->append_fields($field);
     }
     foreach my $field ($item_marc->fields()) {
         $biblio_marc->append_fields($field);
     }
@@ -1846,7 +2146,7 @@ replace it with the tag from C<$item_marc>.
 sub _replace_item_field_in_biblio {
     my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
     my $dbh = C4::Context->dbh;
 sub _replace_item_field_in_biblio {
     my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
     my $dbh = C4::Context->dbh;
-    
+
     # get complete MARC record & replace the item field by the new one
     my $completeRecord = GetMarcBiblio($biblionumber);
     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
     # get complete MARC record & replace the item field by the new one
     my $completeRecord = GetMarcBiblio($biblionumber);
     my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
@@ -1897,37 +2197,89 @@ sub _repack_item_errors {
     return @repacked_errors;
 }
 
     return @repacked_errors;
 }
 
-=head2 _add_unlinked_marc_fields
+=head2 _get_unlinked_item_subfields
 
 
-Adds marc fields to new_item_marc 
-from $item_marc_record fields which 
-1) are not linked to items table 
-2) but still are used in framework 
-3) and are provided a value
+=over 4
+
+my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
+
+=back
+
+=cut
+
+sub _get_unlinked_item_subfields {
+    my $original_item_marc = shift;
+    my $frameworkcode = shift;
+
+    my $marcstructure = GetMarcStructure(1, $frameworkcode);
+
+    # assume that this record has only one field, and that that
+    # field contains only the item information
+    my $subfields = [];
+    my @fields = $original_item_marc->fields();
+    if ($#fields > -1) {
+        my $field = $fields[0];
+           my $tag = $field->tag();
+        foreach my $subfield ($field->subfields()) {
+            if (defined $subfield->[1] and
+                $subfield->[1] ne '' and
+                !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
+                push @$subfields, $subfield->[0] => $subfield->[1];
+            }
+        }
+    }
+    return $subfields;
+}
+
+=head2 _get_unlinked_subfields_xml
+
+=over 4
+
+my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
+
+=back
 
 =cut
 
 =cut
-sub _add_unlinked_marc_fields{
-    my $new_item_marc= shift;
-    my $item_marc_record= shift;
-    my $frameworkcode= shift;
-    my $tmp_item_marc=$new_item_marc->clone;  
-    my $marcstructure=GetMarcStructure(1,$frameworkcode);
-    foreach my $field ($item_marc_record->fields()){
-       my $tag=$field->tag();
-        if ($new_item_marc->fields($tag)){
-        # It is assumed  that item marc records only have ***one*** tag and that this tag is mandatory.
-        # So new_item_marc MUST have $field->tag    
-            foreach my $subfield ($field->subfields()){
-                if (!$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'} && !$tmp_item_marc->subfield($tag,$subfield->[0])){
-                       #$new_item_marc->field($tag)->add_subfields($subfield->[0]=>$subfield->[1]);
-
-                   my $ret= eval {$new_item_marc->field($tag)->add_subfields($subfield->[0]=>$subfield->[1])};
-                   if ($@ or !$ret) {warn $subfield->[0]."=>".$subfield->[1]."\n".$new_item_marc->as_formatted."\n".$item_marc_record;}
-                }          
-            }       
-        }   
+
+sub _get_unlinked_subfields_xml {
+    my $unlinked_item_subfields = shift;
+
+    my $xml;
+    if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
+        my $marc = MARC::Record->new();
+        # use of tag 999 is arbitrary, and doesn't need to match the item tag
+        # used in the framework
+        $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
+        $marc->encoding("UTF-8");    
+        $xml = $marc->as_xml("USMARC");
+    }
+
+    return $xml;
+}
+
+=head2 _parse_unlinked_item_subfields_from_xml
+
+=over 4
+
+my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
+
+=back
+
+=cut
+
+sub  _parse_unlinked_item_subfields_from_xml {
+    my $xml = shift;
+
+    return unless defined $xml and $xml ne "";
+    my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
+    my $unlinked_subfields = [];
+    my @fields = $marc->fields();
+    if ($#fields > -1) {
+        foreach my $subfield ($fields[0]->subfields()) {
+            push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
+        }
     }
     }
-    return $new_item_marc;      
+    return $unlinked_subfields;
 }
 
 1;
 }
 
 1;