Bug 3264 UnCloneField() / minus button in MARC editor can clear all subfields (author...
[koha_gimpoz] / C4 / Biblio.pm
old mode 100755 (executable)
new mode 100644 (file)
index 7b72411..8d2dd9d
@@ -2,6 +2,7 @@ package C4::Biblio;
 
 # Copyright 2000-2002 Katipo Communications
 # Copyright 2010 BibLibre
+# Copyright 2011 Equinox Software, Inc.
 #
 # This file is part of Koha.
 #
@@ -20,6 +21,7 @@ package C4::Biblio;
 
 use strict;
 use warnings;
+use Carp;
 
 # use utf8;
 use MARC::Record;
@@ -35,6 +37,7 @@ use C4::ClassSource;
 use C4::Charset;
 require C4::Heading;
 require C4::Serials;
+require C4::Items;
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -75,18 +78,25 @@ BEGIN {
       &GetMarcBiblio
       &GetMarcAuthors
       &GetMarcSeries
+      &GetMarcHosts
       GetMarcUrls
       &GetUsedMarcStructure
       &GetXmlBiblio
       &GetCOinSBiblio
+      &GetMarcPrice
+      &GetMarcQuantity
 
       &GetAuthorisedValueDesc
       &GetMarcStructure
       &GetMarcFromKohaField
       &GetFrameworkCode
       &TransformKohaToMarc
+      &PrepHostMarcField
 
       &CountItemsIssued
+      &CountBiblioInOrders
+      &GetSubscriptionsId
+      &GetHolds
     );
 
     # To modify something
@@ -133,10 +143,11 @@ eval {
         import Memoize::Memcached qw(memoize_memcached);
 
         my $memcached = {
-            servers    => [$servers],
-            key_prefix => C4::Context->config('memcached_namespace') || 'koha',
-        };
-        memoize_memcached( 'GetMarcStructure', memcached => $memcached, expire_time => 600 );    #cache for 10 minutes
+            servers     => [$servers],
+            key_prefix  => C4::Context->config('memcached_namespace') || 'koha',
+            expire_time => 600
+        }; # cache for 10 mins, if you want to cache for different make a different memcached hash
+        memoize_memcached( 'GetMarcStructure', memcached => $memcached );
     }
 };
 
@@ -290,58 +301,30 @@ and biblionumber data for indexing.
 
 sub ModBiblio {
     my ( $record, $biblionumber, $frameworkcode ) = @_;
+    croak "No record" unless $record;
+
     if ( C4::Context->preference("CataloguingLog") ) {
         my $newrecord = GetMarcBiblio($biblionumber);
         logaction( "CATALOGUING", "MODIFY", $biblionumber, "BEFORE=>" . $newrecord->as_formatted );
     }
 
-    SetUTF8Flag($record);
-    my $dbh = C4::Context->dbh;
-
-    $frameworkcode = "" unless $frameworkcode;
-
-    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
-    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
-    my $oldRecord = GetMarcBiblio($biblionumber);
-
-    # delete any item fields from incoming record to avoid
-    # duplication or incorrect data - use AddItem() or ModItem()
-    # to change items
-    foreach my $field ( $record->field($itemtag) ) {
-        $record->delete_field($field);
-    }
-
-    # parse each item, and, for an unknown reason, re-encode each subfield
-    # if you don't do that, the record will have encoding mixed
-    # and the biblio will be re-encoded.
-    # strange, I (Paul P.) searched more than 1 day to understand what happends
-    # but could only solve the problem this way...
-    my @fields = $oldRecord->field($itemtag);
-    foreach my $fielditem (@fields) {
-        my $field;
-        foreach ( $fielditem->subfields() ) {
-            # re-encode the subfield only if it isn't already in utf-8.
-            my ($tag, $value) = @$_;
-            $tag = Encode::encode('utf-8', $tag) unless utf8::is_utf8($tag);
-            $value = Encode::encode('utf-8', $value) unless utf8::is_utf8($value);
-
-            if ($field) {
-                $field->add_subfields( $tag => $value );
-            } else {
-                $field = MARC::Field->new( "$itemtag", '', '', $tag => $value );
-            }
-        }
-        $record->append_fields($field);
-    }
-
+    # Cleaning up invalid fields must be done early or SetUTF8Flag is liable to
+    # throw an exception which probably won't be handled.
     foreach my $field ($record->fields()) {
         if (! $field->is_control_field()) {
-            if (scalar($field->subfields()) == 0) {
-                $record->delete_fields($field);
+            if (scalar($field->subfields()) == 0 || (scalar($field->subfields()) == 1 && $field->subfield('9'))) {
+                $record->delete_field($field);
             }
         }
     }
 
+    SetUTF8Flag($record);
+    my $dbh = C4::Context->dbh;
+
+    $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
+
+    _strip_item_fields($record, $frameworkcode);
+
     # update biblionumber and biblioitemnumber in MARC
     # FIXME - this is assuming a 1 to 1 relationship between
     # biblios and biblioitems
@@ -366,6 +349,29 @@ sub ModBiblio {
     return 1;
 }
 
+=head2 _strip_item_fields
+
+  _strip_item_fields($record, $frameworkcode)
+
+Utility routine to remove item tags from a
+MARC bib.
+
+=cut
+
+sub _strip_item_fields {
+    my $record = shift;
+    my $frameworkcode = shift;
+    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
+    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
+
+    # delete any item fields from incoming record to avoid
+    # duplication or incorrect data - use AddItem() or ModItem()
+    # to change items
+    foreach my $field ( $record->field($itemtag) ) {
+        $record->delete_field($field);
+    }
+}
+
 =head2 ModBiblioframework
 
    ModBiblioframework($biblionumber,$frameworkcode);
@@ -384,7 +390,7 @@ sub ModBiblioframework {
 
 =head2 DelBiblio
 
-  my $error = &DelBiblio($dbh,$biblionumber);
+  my $error = &DelBiblio($biblionumber);
 
 Exported function (core API) for deleting a biblio in koha.
 Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)
@@ -758,7 +764,8 @@ Return the ISBD view which can be included in opac and intranet
 
 sub GetISBDView {
     my ( $biblionumber, $template ) = @_;
-    my $record   = GetMarcBiblio($biblionumber);
+    my $record   = GetMarcBiblio($biblionumber, 1);
+    return undef unless defined $record;
     my $itemtype = &GetFrameworkCode($biblionumber);
     my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
     my $tagslib = &GetMarcStructure( 1, $itemtype );
@@ -1037,23 +1044,28 @@ for the given frameworkcode
 
 sub GetMarcFromKohaField {
     my ( $kohafield, $frameworkcode ) = @_;
-    return 0, 0 unless $kohafield and defined $frameworkcode;
+    return (0, undef) unless $kohafield and defined $frameworkcode;
     my $relations = C4::Context->marcfromkohafield;
-    return ( $relations->{$frameworkcode}->{$kohafield}->[0], $relations->{$frameworkcode}->{$kohafield}->[1] );
+    if ( my $mf = $relations->{$frameworkcode}->{$kohafield} ) {
+        return @$mf;
+    }
+    return (0, undef);
 }
 
 =head2 GetMarcBiblio
 
-  my $record = GetMarcBiblio($biblionumber);
+  my $record = GetMarcBiblio($biblionumber, [$embeditems]);
 
 Returns MARC::Record representing bib identified by
 C<$biblionumber>.  If no bib exists, returns undef.
-The MARC record contains both biblio & item data.
+C<$embeditems>.  If set to true, items data are included.
+The MARC record contains biblio data, and items data if $embeditems is set to true.
 
 =cut
 
 sub GetMarcBiblio {
     my $biblionumber = shift;
+    my $embeditems   = shift || 0;
     my $dbh          = C4::Context->dbh;
     my $sth          = $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
     $sth->execute($biblionumber);
@@ -1065,8 +1077,11 @@ sub GetMarcBiblio {
     if ($marcxml) {
         $record = eval { MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour') ) };
         if ($@) { warn " problem with :$biblionumber : $@ \n$marcxml"; }
+        return unless $record;
+
+        C4::Biblio::_koha_marc_update_bib_ids($record, '', $biblionumber, $biblionumber);
+       C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber) if ($embeditems);
 
-        #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
         return $record;
     } else {
         return undef;
@@ -1093,20 +1108,17 @@ sub GetXmlBiblio {
 
 =head2 GetCOinSBiblio
 
-  my $coins = GetCOinSBiblio($biblionumber);
+  my $coins = GetCOinSBiblio($record);
 
-Returns the COinS(a span) which can be included in a biblio record
+Returns the COinS (a span) which can be included in a biblio record
 
 =cut
 
 sub GetCOinSBiblio {
-    my ($biblionumber) = @_;
-    my $record = GetMarcBiblio($biblionumber);
+    my $record = shift;
 
     # get the coin format
     if ( ! $record ) {
-       # can't get a valid MARC::Record object, bail out at this point
-       warn "We called GetMarcBiblio with a biblionumber that doesn't exist biblionumber=$biblionumber";
        return;
     }
     my $pos7 = substr $record->leader(), 7, 1;
@@ -1174,8 +1186,8 @@ sub GetCOinSBiblio {
     if ( C4::Context->preference("marcflavour") eq "UNIMARC" ) {
 
         # Setting datas
-        $aulast  = $record->subfield( '700', 'a' );
-        $aufirst = $record->subfield( '700', 'b' );
+        $aulast  = $record->subfield( '700', 'a' ) || '';
+        $aufirst = $record->subfield( '700', 'b' ) || '';
         $oauthors = "&amp;rft.au=$aufirst $aulast";
 
         # others authors
@@ -1188,10 +1200,10 @@ sub GetCOinSBiblio {
           ( $mtx eq 'dc' )
           ? "&amp;rft.title=" . $record->subfield( '200', 'a' )
           : "&amp;rft.title=" . $record->subfield( '200', 'a' ) . "&amp;rft.btitle=" . $record->subfield( '200', 'a' );
-        $pubyear   = $record->subfield( '210', 'd' );
-        $publisher = $record->subfield( '210', 'c' );
-        $isbn      = $record->subfield( '010', 'a' );
-        $issn      = $record->subfield( '011', 'a' );
+        $pubyear   = $record->subfield( '210', 'd' ) || '';
+        $publisher = $record->subfield( '210', 'c' ) || '';
+        $isbn      = $record->subfield( '010', 'a' ) || '';
+        $issn      = $record->subfield( '011', 'a' ) || '';
     } else {
 
         # MARC21 need some improve
@@ -1211,7 +1223,8 @@ sub GetCOinSBiblio {
         $subtitle = $record->subfield( '245', 'b' ) || '';
         $title .= $subtitle;
         if ($titletype eq 'a') {
-            $pubyear   = substr $record->field('008')->data(), 7, 4;
+            $pubyear   = $record->field('008') || '';
+            $pubyear   = substr($pubyear->data(), 7, 4) if $pubyear;
             $isbn      = $record->subfield( '773', 'z' ) || '';
             $issn      = $record->subfield( '773', 'x' ) || '';
             if ($mtx eq 'journal') {
@@ -1243,6 +1256,72 @@ sub GetCOinSBiblio {
     return $coins_value;
 }
 
+
+=head2 GetMarcPrice
+
+return the prices in accordance with the Marc format.
+=cut
+
+sub GetMarcPrice {
+    my ( $record, $marcflavour ) = @_;
+    my @listtags;
+    my $subfield;
+    
+    if ( $marcflavour eq "MARC21" ) {
+        @listtags = ('345', '020');
+        $subfield="c";
+    } elsif ( $marcflavour eq "UNIMARC" ) {
+        @listtags = ('345', '010');
+        $subfield="d";
+    } else {
+        return;
+    }
+    
+    for my $field ( $record->field(@listtags) ) {
+        for my $subfield_value  ($field->subfield($subfield)){
+            #check value
+            return $subfield_value if ($subfield_value);
+        }
+    }
+    return 0; # no price found
+}
+
+=head2 GetMarcQuantity
+
+return the quantity of a book. Used in acquisition only, when importing a file an iso2709 from a bookseller
+Warning : this is not really in the marc standard. In Unimarc, Electre (the most widely used bookseller) use the 969$a
+
+=cut
+
+sub GetMarcQuantity {
+    my ( $record, $marcflavour ) = @_;
+    my @listtags;
+    my $subfield;
+    
+    if ( $marcflavour eq "MARC21" ) {
+        return 0
+    } elsif ( $marcflavour eq "UNIMARC" ) {
+        @listtags = ('969');
+        $subfield="a";
+    } else {
+        return;
+    }
+    
+    for my $field ( $record->field(@listtags) ) {
+        for my $subfield_value  ($field->subfield($subfield)){
+            #check value
+            if ($subfield_value) {
+                 # in France, the cents separator is the , but sometimes, ppl use a .
+                 # in this case, the price will be x100 when unformatted ! Replace the . by a , to get a proper price calculation
+                $subfield_value =~ s/\./,/ if C4::Context->preference("CurrencyFormat") eq "FR";
+                return $subfield_value;
+            }
+        }
+    }
+    return 0; # no price found
+}
+
+
 =head2 GetAuthorisedValueDesc
 
   my $subfieldvalue =get_authorised_value_desc(
@@ -1441,7 +1520,10 @@ sub GetMarcSubjects {
             my $value     = $subject_subfield->[1];
             my $linkvalue = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $counter == 0;
+            my $operator;
+            if ( $counter != 0 ) {
+                $operator = ' and ';
+            }
             if ( $code eq 9 ) {
                 $found9 = 1;
                 @link_loop = ( { 'limit' => 'an', link => "$linkvalue" } );
@@ -1449,7 +1531,10 @@ sub GetMarcSubjects {
             if ( not $found9 ) {
                 push @link_loop, { 'limit' => $subject_limit, link => $linkvalue, operator => $operator };
             }
-            my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
+            my $separator;
+            if ( $counter != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
 
             # ignore $9
             my @this_link_loop = @link_loop;
@@ -1507,7 +1592,10 @@ sub GetMarcAuthors {
             my $value        = $authors_subfield->[1];
             my $linkvalue    = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $count_auth == 0;
+            my $operator;
+            if ( $count_auth != 0 ) {
+                $operator = ' and ';
+            }
 
             # if we have an authority link, use that as the link, otherwise use standard searching
             if ($subfield9) {
@@ -1523,8 +1611,18 @@ sub GetMarcAuthors {
             $value = GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib )
               if ( $marcflavour eq 'UNIMARC' and ( $authors_subfield->[0] =~ /4/ ) );
             my @this_link_loop = @link_loop;
-            my $separator = C4::Context->preference("authoritysep") unless $count_auth == 0;
-            push @subfields_loop, { code => $subfieldcode, value => $value, link_loop => \@this_link_loop, separator => $separator } unless ( $authors_subfield->[0] eq '9' );
+            my $separator;
+            if ( $count_auth != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
+            push @subfields_loop,
+              { tag       => $field->tag(),
+                code      => $subfieldcode,
+                value     => $value,
+                link_loop => \@this_link_loop,
+                separator => $separator
+              }
+              unless ( $authors_subfield->[0] eq '9' );
             $count_auth++;
         }
         push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
@@ -1635,13 +1733,27 @@ sub GetMarcSeries {
             my $value     = $series_subfield->[1];
             my $linkvalue = $value;
             $linkvalue =~ s/(\(|\))//g;
-            my $operator = " and " unless $counter == 0;
-            push @link_loop, { link => $linkvalue, operator => $operator };
-            my $separator = C4::Context->preference("authoritysep") unless $counter == 0;
+            if ( $counter != 0 ) {
+                push @link_loop, { link => $linkvalue, operator => ' and ', };
+            } else {
+                push @link_loop, { link => $linkvalue, operator => undef, };
+            }
+            my $separator;
+            if ( $counter != 0 ) {
+                $separator = C4::Context->preference('authoritysep');
+            }
             if ($volume_number) {
                 push @subfields_loop, { volumenum => $value };
             } else {
-                push @subfields_loop, { code => $code, value => $value, link_loop => \@link_loop, separator => $separator, volumenum => $volume_number } unless ( $series_subfield->[0] eq '9' );
+                if ( $series_subfield->[0] ne '9' ) {
+                    push @subfields_loop, {
+                        code      => $code,
+                        value     => $value,
+                        link_loop => \@link_loop,
+                        separator => $separator,
+                        volumenum => $volume_number,
+                    };
+                }
             }
             $counter++;
         }
@@ -1656,6 +1768,48 @@ sub GetMarcSeries {
     return $marcseriessarray;
 }    #end getMARCseriess
 
+=head2 GetMarcHosts
+
+  $marchostsarray = GetMarcHosts($record,$marcflavour);
+
+Get all host records (773s MARC21, 461 UNIMARC) from the MARC record and returns them in an array.
+
+=cut
+
+sub GetMarcHosts {
+    my ( $record, $marcflavour ) = @_;
+    my ( $tag,$title_subf,$bibnumber_subf,$itemnumber_subf);
+    $marcflavour ||="MARC21";
+    if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
+        $tag = "773";
+        $title_subf = "t";
+        $bibnumber_subf ="0";
+        $itemnumber_subf='9';
+    }
+    elsif ($marcflavour eq "UNIMARC") {
+        $tag = "461";
+        $title_subf = "t";
+        $bibnumber_subf ="0";
+        $itemnumber_subf='9';
+    };
+
+    my @marchosts;
+
+    foreach my $field ( $record->field($tag)) {
+
+        my @fields_loop;
+
+        my $hostbiblionumber = $field->subfield("$bibnumber_subf");
+        my $hosttitle = $field->subfield($title_subf);
+        my $hostitemnumber=$field->subfield($itemnumber_subf);
+        push @fields_loop, { hostbiblionumber => $hostbiblionumber, hosttitle => $hosttitle, hostitemnumber => $hostitemnumber};
+        push @marchosts, { MARCHOSTS_FIELDS_LOOP => \@fields_loop };
+
+        }
+    my $marchostsarray = \@marchosts;
+    return $marchostsarray;
+}
+
 =head2 GetFrameworkCode
 
   $frameworkcode = GetFrameworkCode( $biblionumber )
@@ -1678,54 +1832,111 @@ sub GetFrameworkCode {
 This function builds partial MARC::Record from a hash
 Hash entries can be from biblio or biblioitems.
 
-This function is called in acquisition module, to create a basic catalogue entry from user entry
+This function is called in acquisition module, to create a basic catalogue
+entry from user entry
 
 =cut
 
+
 sub TransformKohaToMarc {
-    my ($hash) = @_;
-    my $sth    = C4::Context->dbh->prepare( "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?" );
+    my $hash = shift;
     my $record = MARC::Record->new();
     SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
-    foreach ( keys %{$hash} ) {
-        &TransformKohaToMarcOneField( $sth, $record, $_, $hash->{$_}, '' );
+    my $db_to_marc = C4::Context->marcfromkohafield;
+    while ( my ($name, $value) = each %$hash ) {
+        next unless my $dtm = $db_to_marc->{''}->{$name};
+        my ($tag, $letter) = @$dtm;
+        foreach my $value ( split(/\s?\|\s?/, $value, -1) ) {
+            if ( my $field = $record->field($tag) ) {
+                $field->add_subfields( $letter => $value );
+            }
+            else {
+                $record->insert_fields_ordered( MARC::Field->new(
+                    $tag, " ", " ", $letter => $value ) );
+            }
+        }
+
     }
     return $record;
 }
 
-=head2 TransformKohaToMarcOneField
+=head2 PrepHostMarcField
 
-    $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
+    $hostfield = PrepHostMarcField ( $hostbiblionumber,$hostitemnumber,$marcflavour )
 
-=cut
+This function returns a host field populated with data from the host record, the field can then be added to an analytical record
 
-sub TransformKohaToMarcOneField {
-    my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
-    $frameworkcode = '' unless $frameworkcode;
-    my $tagfield;
-    my $tagsubfield;
+=cut
 
-    if ( !defined $sth ) {
-        my $dbh = C4::Context->dbh;
-        $sth = $dbh->prepare( "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?" );
-    }
-    $sth->execute( $frameworkcode, $kohafieldname );
-    if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
-        my @values = split(/\s?\|\s?/, $value, -1);
-        
-        foreach my $itemvalue (@values){
-        my $tag = $record->field($tagfield);
-        if ($tag) {
-                $tag->add_subfields( $tagsubfield => $itemvalue );
-            $record->delete_field($tag);
-            $record->insert_fields_ordered($tag);
-            }
-            else {
-                $record->add_fields( $tagfield, " ", " ", $tagsubfield => $itemvalue );
+sub PrepHostMarcField {
+    my ($hostbiblionumber,$hostitemnumber, $marcflavour) = @_;
+    $marcflavour ||="MARC21";
+    
+    my $hostrecord = GetMarcBiblio($hostbiblionumber);
+       my $item = C4::Items::GetItem($hostitemnumber);
+       
+       my $hostmarcfield;
+    if ( $marcflavour eq "MARC21" || $marcflavour eq "NORMARC" ) {
+       
+        #main entry
+        my $mainentry;
+        if ($hostrecord->subfield('100','a')){
+            $mainentry = $hostrecord->subfield('100','a');
+        } elsif ($hostrecord->subfield('110','a')){
+            $mainentry = $hostrecord->subfield('110','a');
+        } else {
+            $mainentry = $hostrecord->subfield('111','a');
+        }
+       
+        # qualification info
+        my $qualinfo;
+        if (my $field260 = $hostrecord->field('260')){
+            $qualinfo =  $field260->as_string( 'abc' );
+        }
+       
+
+       #other fields
+        my $ed = $hostrecord->subfield('250','a');
+        my $barcode = $item->{'barcode'};
+        my $title = $hostrecord->subfield('245','a');
+
+        # record control number, 001 with 003 and prefix
+        my $recctrlno;
+        if ($hostrecord->field('001')){
+            $recctrlno = $hostrecord->field('001')->data();
+            if ($hostrecord->field('003')){
+                $recctrlno = '('.$hostrecord->field('003')->data().')'.$recctrlno;
             }
         }
-    }
-    return $record;
+
+        # issn/isbn
+        my $issn = $hostrecord->subfield('022','a');
+        my $isbn = $hostrecord->subfield('020','a');
+
+
+        $hostmarcfield = MARC::Field->new(
+                773, '0', '',
+                '0' => $hostbiblionumber,
+                '9' => $hostitemnumber,
+                'a' => $mainentry,
+                'b' => $ed,
+                'd' => $qualinfo,
+                'o' => $barcode,
+                't' => $title,
+                'w' => $recctrlno,
+                'x' => $issn,
+                'z' => $isbn
+                );
+    } elsif ($marcflavour eq "UNIMARC") {
+        $hostmarcfield = MARC::Field->new(
+            461, '', '',
+            '0' => $hostbiblionumber,
+            't' => $hostrecord->subfield('200','a'), 
+            '9' => $hostitemnumber
+        );     
+    };
+
+    return $hostmarcfield;
 }
 
 =head2 TransformHtmlToXml
@@ -1848,7 +2059,7 @@ sub TransformHtmlToXml {
         }
         $prevtag = @$tags[$i];
     }
-    $xml .= "</datafield>\n" if @$tags > 0;
+    $xml .= "</datafield>\n" if $xml =~ m/<datafield/;
     if ( C4::Context->preference('marcflavour') eq 'UNIMARC' and !$unimarc_and_100_exist ) {
 
         #     warn "SETTING 100 for $auth_type";
@@ -1885,8 +2096,8 @@ sub _default_ind_to_space {
 
 =head2 TransformHtmlToMarc
 
-    L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
-    L<$params> is a ref to an array as below:
+    L<$record> = TransformHtmlToMarc(L<$cgi>)
+    L<$cgi> is the CGI object which containts the values for subfields
     {
         'tag_010_indicator1_531951' ,
         'tag_010_indicator2_531951' ,
@@ -1903,15 +2114,15 @@ sub _default_ind_to_space {
         'tag_200_code_f_873510_110730' ,
         'tag_200_subfield_f_873510_110730' ,
     }
-    L<$cgi> is the CGI object which containts the value.
     L<$record> is the MARC::Record object.
 
 =cut
 
 sub TransformHtmlToMarc {
-    my $params = shift;
     my $cgi    = shift;
 
+    my @params = $cgi->param();
+
     # explicitly turn on the UTF-8 flag for all
     # 'tag_' parameters to avoid incorrect character
     # conversion later on
@@ -1931,8 +2142,9 @@ sub TransformHtmlToMarc {
     my $record = MARC::Record->new();
     my $i      = 0;
     my @fields;
-    while ( $params->[$i] ) {    # browse all CGI params
-        my $param    = $params->[$i];
+#FIXME This code assumes that the CGI params will be in the same order as the fields in the template; this is no absolute guarantee!
+    while ( $params[$i] ) {    # browse all CGI params
+        my $param    = $params[$i];
         my $newfield = 0;
 
         # if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
@@ -1948,7 +2160,7 @@ sub TransformHtmlToMarc {
             my $tag = $1;
 
             my $ind1 = _default_ind_to_space( substr( $cgi->param($param), 0, 1 ) );
-            my $ind2 = _default_ind_to_space( substr( $cgi->param( $params->[ $i + 1 ] ), 0, 1 ) );
+            my $ind2 = _default_ind_to_space( substr( $cgi->param( $params[ $i + 1 ] ), 0, 1 ) );
             $newfield = 0;
             my $j = $i + 2;
 
@@ -1958,31 +2170,35 @@ sub TransformHtmlToMarc {
                     # Force a fake leader even if not provided to avoid crashing
                     # during decoding MARC record containing UTF-8 characters
                     $record->leader(
-                        length( $cgi->param($params->[$j+1]) ) == 24
-                        ? $cgi->param( $params->[ $j + 1 ] )
+                        length( $cgi->param($params[$j+1]) ) == 24
+                        ? $cgi->param( $params[ $j + 1 ] )
                         : '     nam a22        4500'
                        )
                     ;
                     # between 001 and 009 (included)
-                } elsif ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {
-                    $newfield = MARC::Field->new( $tag, $cgi->param( $params->[ $j + 1 ] ), );
+                } elsif ( $cgi->param( $params[ $j + 1 ] ) ne '' ) {
+                    $newfield = MARC::Field->new( $tag, $cgi->param( $params[ $j + 1 ] ), );
                 }
 
                 # > 009, deal with subfields
             } else {
-                while ( defined $params->[$j] && $params->[$j] =~ /_code_/ ) {    # browse all it's subfield
-                    my $inner_param = $params->[$j];
-                    if ($newfield) {
-                        if ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {         # only if there is a value (code => value)
-                            $newfield->add_subfields( $cgi->param($inner_param) => $cgi->param( $params->[ $j + 1 ] ) );
-                        }
-                    } else {
-                        if ( $cgi->param( $params->[ $j + 1 ] ) ne '' ) {         # creating only if there is a value (code => value)
-                            $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($inner_param) => $cgi->param( $params->[ $j + 1 ] ), );
-                        }
+                # browse subfields for this tag (reason for _code_ match)
+                while(defined $params[$j] && $params[$j] =~ /_code_/) {
+                    last unless defined $params[$j+1];
+                    #if next param ne subfield, then it was probably empty
+                    #try next param by incrementing j
+                    if($params[$j+1]!~/_subfield_/) {$j++; next; }
+                    my $fval= $cgi->param($params[$j+1]);
+                    #check if subfield value not empty and field exists
+                    if($fval ne '' && $newfield) {
+                        $newfield->add_subfields( $cgi->param($params[$j]) => $fval);
+                    }
+                    elsif($fval ne '') {
+                        $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($params[$j]) => $fval );
                     }
                     $j += 2;
-                }
+                } #end-of-while
+                $i= $j-1; #update i for outer loop accordingly
             }
             push @fields, $newfield if ($newfield);
         }
@@ -2257,8 +2473,11 @@ sub PrepareItemrecordDisplay {
     my $tagslib = &GetMarcStructure( 1, $frameworkcode );
 
     # return nothing if we don't have found an existing framework.
-    return "" unless $tagslib;
-    my $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum ) if ($itemnum);
+    return q{} unless $tagslib;
+    my $itemrecord;
+    if ($itemnum) {
+        $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
+    }
     my @loop_data;
     my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" );
     foreach my $tag ( sort keys %{$tagslib} ) {
@@ -2297,15 +2516,20 @@ sub PrepareItemrecordDisplay {
                     && C4::Context->preference('itemcallnumber') ) {
                     my $CNtag      = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
                     my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
-                    my $temp = $itemrecord->field($CNtag) if ($itemrecord);
-                    if ($temp) {
-                        $defaultvalue = $temp->subfield($CNsubfield);
+                    if ($itemrecord) {
+                        my $temp = $itemrecord->field($CNtag);
+                        if ($temp) {
+                            $defaultvalue = $temp->subfield($CNsubfield);
+                        }
                     }
                 }
                 if (   $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
                     && $defaultvalues
                     && $defaultvalues->{'callnumber'} ) {
-                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    my $temp;
+                    if ($itemrecord) {
+                        $temp = $itemrecord->field($subfield);
+                    }
                     unless ($temp) {
                         $defaultvalue = $defaultvalues->{'callnumber'} if $defaultvalues;
                     }
@@ -2313,7 +2537,10 @@ sub PrepareItemrecordDisplay {
                 if (   ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
                     && $defaultvalues
                     && $defaultvalues->{'branchcode'} ) {
-                    my $temp = $itemrecord->field($subfield) if ($itemrecord);
+                    my $temp;
+                    if ($itemrecord) {
+                        $temp = $itemrecord->field($subfield);
+                    }
                     unless ($temp) {
                         $defaultvalue = $defaultvalues->{branchcode} if $defaultvalues;
                     }
@@ -2438,8 +2665,10 @@ sub PrepareItemrecordDisplay {
             }
         }
     }
-    my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
-      if ( $itemrecord && $itemrecord->field($itemtagfield) );
+    my $itemnumber;
+    if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
+        $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
+    }
     return {
         'itemtagfield'    => $itemtagfield,
         'itemtagsubfield' => $itemtagsubfield,
@@ -2586,6 +2815,36 @@ sub GetNoZebraIndexes {
     return %indexes;
 }
 
+=head2 EmbedItemsInMarcBiblio
+
+    EmbedItemsInMarcBiblio($marc, $biblionumber);
+
+Given a MARC::Record object containing a bib record,
+modify it to include the items attached to it as 9XX
+per the bib's MARC framework.
+
+=cut
+
+sub EmbedItemsInMarcBiblio {
+    my ($marc, $biblionumber) = @_;
+    croak "No MARC record" unless $marc;
+
+    my $frameworkcode = GetFrameworkCode($biblionumber);
+    _strip_item_fields($marc, $frameworkcode);
+
+    # ... and embed the current items
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
+    $sth->execute($biblionumber);
+    my @item_fields;
+    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
+    while (my ($itemnumber) = $sth->fetchrow_array) {
+        my $item_marc = C4::Items::GetMarcItem($biblionumber, $itemnumber);
+        push @item_fields, $item_marc->field($itemtag);
+    }
+    $marc->append_fields(@item_fields);
+}
+
 =head1 INTERNAL FUNCTIONS
 
 =head2 _DelBiblioNoZebra($biblionumber,$record,$server);
@@ -2907,9 +3166,24 @@ sub _koha_marc_update_bib_ids {
     # we drop the original field
     # we add the new builded field.
     my ( $biblio_tag,     $biblio_subfield )     = GetMarcFromKohaField( "biblio.biblionumber",          $frameworkcode );
+    die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
     my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
+    die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblio_tag;
+
+    if ( $biblio_tag == $biblioitem_tag ) {
+
+        # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
+        my $new_field = MARC::Field->new(
+            $biblio_tag, '', '',
+            "$biblio_subfield"     => $biblionumber,
+            "$biblioitem_subfield" => $biblioitemnumber
+        );
 
-    if ( $biblio_tag != $biblioitem_tag ) {
+        # drop old field and create new one...
+        my $old_field = $record->field($biblio_tag);
+        $record->delete_field($old_field) if $old_field;
+        $record->insert_fields_ordered($new_field);
+    } else {
 
         # biblionumber & biblioitemnumber are in different fields
 
@@ -2924,7 +3198,7 @@ sub _koha_marc_update_bib_ids {
         # drop old field and create new one...
         $old_field = $record->field($biblio_tag);
         $record->delete_field($old_field) if $old_field;
-        $record->append_fields($new_field);
+        $record->insert_fields_ordered($new_field);
 
         # deal with biblioitemnumber
         if ( $biblioitem_tag < 10 ) {
@@ -2937,20 +3211,6 @@ sub _koha_marc_update_bib_ids {
         $old_field = $record->field($biblioitem_tag);
         $record->delete_field($old_field) if $old_field;
         $record->insert_fields_ordered($new_field);
-
-    } else {
-
-        # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
-        my $new_field = MARC::Field->new(
-            $biblio_tag, '', '',
-            "$biblio_subfield"     => $biblionumber,
-            "$biblioitem_subfield" => $biblioitemnumber
-        );
-
-        # drop old field and create new one...
-        my $old_field = $record->field($biblio_tag);
-        $record->delete_field($old_field) if $old_field;
-        $record->insert_fields_ordered($new_field);
     }
 }
 
@@ -3247,9 +3507,12 @@ sub _koha_delete_biblio {
         $bkup_sth->finish;
 
         # delete the biblio
-        my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
-        $del_sth->execute($biblionumber);
-        $del_sth->finish;
+        my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
+        $sth2->execute($biblionumber);
+        # update the timestamp (Bugzilla 7146)
+        $sth2= $dbh->prepare("UPDATE deletedbiblio SET timestamp=NOW() WHERE biblionumber=?");
+        $sth2->execute($biblionumber);
+        $sth2->finish;
     }
     $sth->finish;
     return undef;
@@ -3293,9 +3556,12 @@ sub _koha_delete_biblioitems {
         $bkup_sth->finish;
 
         # delete the biblioitem
-        my $del_sth = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
-        $del_sth->execute($biblioitemnumber);
-        $del_sth->finish;
+        my $sth2 = $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
+        $sth2->execute($biblioitemnumber);
+        # update the timestamp (Bugzilla 7146)
+        $sth2= $dbh->prepare("UPDATE deletedbiblioitems SET timestamp=NOW() WHERE biblioitemnumber=?");
+        $sth2->execute($biblioitemnumber);
+        $sth2->finish;
     }
     $sth->finish;
     return undef;
@@ -3340,7 +3606,7 @@ sub ModBiblioMarc {
         }
         substr( $string, 22, 6, "frey50" );
         unless ( $record->subfield( 100, "a" ) ) {
-            $record->insert_grouped_field( MARC::Field->new( 100, "", "", "a" => $string ) );
+            $record->insert_fields_ordered( MARC::Field->new( 100, "", "", "a" => $string ) );
         }
     }
 
@@ -3534,6 +3800,76 @@ sub get_biblio_authorised_values {
     return $authorised_values;
 }
 
+=head2 CountBiblioInOrders
+
+=over 4
+$count = &CountBiblioInOrders( $biblionumber);
+
+=back
+
+This function return count of biblios in orders with $biblionumber 
+
+=cut
+
+sub CountBiblioInOrders {
+ my ($biblionumber) = @_;
+    my $dbh            = C4::Context->dbh;
+    my $query          = "SELECT count(*)
+          FROM  aqorders 
+          WHERE biblionumber=? AND (datecancellationprinted IS NULL OR datecancellationprinted='0000-00-00')";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber);
+    my $count = $sth->fetchrow;
+    return ($count);
+}
+
+=head2 GetSubscriptionsId
+
+=over 4
+$subscriptions = &GetSubscriptionsId($biblionumber);
+
+=back
+
+This function return an array of subscriptionid with $biblionumber
+
+=cut
+
+sub GetSubscriptionsId {
+ my ($biblionumber) = @_;
+    my $dbh            = C4::Context->dbh;
+    my $query          = "SELECT subscriptionid
+          FROM  subscription
+          WHERE biblionumber=?";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber);
+    my @subscriptions = $sth->fetchrow_array;
+    return (@subscriptions);
+}
+
+=head2 GetHolds
+
+=over 4
+$holds = &GetHolds($biblionumber);
+
+=back
+
+This function return the count of holds with $biblionumber
+
+=cut
+
+sub GetHolds {
+ my ($biblionumber) = @_;
+    my $dbh            = C4::Context->dbh;
+    my $query          = "SELECT count(*)
+          FROM  reserves
+          WHERE biblionumber=?";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber);
+    my $holds = $sth->fetchrow;
+    return ($holds);
+}
+
+
 1;
 
 __END__