LDAP - further integration
[koha_fer] / C4 / Biblio.pm
old mode 100644 (file)
new mode 100755 (executable)
index 1b93f0f..83ddd6d
@@ -27,7 +27,7 @@ use MARC::File::USMARC;
 use MARC::File::XML;
 use ZOOM;
 use C4::Koha;
-use C4::Date;
+use C4::Dates qw/format_date/;
 use C4::Log; # logaction
 use C4::ClassSource;
 
@@ -216,61 +216,7 @@ sub AddBiblio {
     $olddata->{'biblionumber'} = $biblionumber;
     ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
 
-    # we must add bibnum and bibitemnum in MARC::Record...
-    # we build the new field with biblionumber and biblioitemnumber
-    # we drop the original field
-    # we add the new builded field.
-    ( my $biblio_tag, my $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
-    ( my $biblioitem_tag, my $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
-
-    my $newfield;
-
-    # biblionumber & biblioitemnumber are in different fields
-    if ( $biblio_tag != $biblioitem_tag ) {
-
-        # deal with biblionumber
-        if ( $biblio_tag < 10 ) {
-            $newfield = MARC::Field->new( $biblio_tag, $biblionumber );
-        }
-        else {
-            $newfield =
-              MARC::Field->new( $biblio_tag, '', '',
-                "$biblio_subfield" => $biblionumber );
-        }
-
-        # drop old field and create new one...
-        my $old_field = $record->field($biblio_tag);
-        $record->delete_field($old_field);
-        $record->append_fields($newfield);
-
-        # deal with biblioitemnumber
-        if ( $biblioitem_tag < 10 ) {
-            $newfield = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
-        }
-        else {
-            $newfield =
-              MARC::Field->new( $biblioitem_tag, '', '',
-                "$biblioitem_subfield" => $biblioitemnumber, );
-        }
-        # drop old field and create new one...
-        $old_field = $record->field($biblioitem_tag);
-        $record->delete_field($old_field);
-        $record->insert_fields_ordered($newfield);
-
-# biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
-    }
-    else {
-        my $newfield = 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);
-        $record->insert_fields_ordered($newfield);
-    }
+    _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
 
     # now add the record
     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
@@ -311,8 +257,8 @@ sub AddItem {
     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
     $sth =
       $dbh->prepare(
-        "SELECT notforloan FROM itemtypes WHERE itemtype='$itemtype'");
-    $sth->execute();
+        "SELECT notforloan FROM itemtypes WHERE itemtype=?");
+    $sth->execute( C4::Context->preference('item-level_itypes') ? $item->{'itype'} : $itemtype );
     my $notforloan = $sth->fetchrow;
     ##Change the notforloan field if $notforloan found
     if ( $notforloan > 0 ) {
@@ -391,14 +337,15 @@ sub ModBiblio {
         $record->append_fields($field);
     }
     
-    # adding biblionumber
-    my ($tag_biblionumber, $subfield_biblionumber) = GetMarcFromKohaField('biblio.biblionumber',$frameworkcode);
-    $record->append_fields(
-           MARC::Field->new(
-                   $tag_biblionumber,'','',$subfield_biblionumber => $biblionumber
-           )
-    ) unless ($record->subfield($tag_biblionumber,$subfield_biblionumber));
-    
+    # update biblionumber and biblioitemnumber in MARC
+    # FIXME - this is assuming a 1 to 1 relationship between
+    # biblios and biblioitems
+    my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
+    $sth->execute($biblionumber);
+    my ($biblioitemnumber) = $sth->fetchrow;
+    $sth->finish();
+    _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
+
     # update the MARC record (that now contains biblio and items) with the new record data
     &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
     
@@ -406,12 +353,8 @@ sub ModBiblio {
     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
 
     # modify the other koha tables
-       use Data::Dumper;
-       warn "OLDBIB:";
-       warn Dumper($oldbiblio);
-
-    _koha_modify_biblio( $dbh, $oldbiblio );
-    _koha_modify_biblioitem( $dbh, $oldbiblio );
+    _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
+    _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
     return 1;
 }
 
@@ -442,6 +385,12 @@ sub ModItem {
         my $frameworkcode = GetFrameworkCode( $biblionumber );
         ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
         my $olditem       = TransformMarcToKoha( $dbh, $record, $frameworkcode,'items');
+        $olditem->{'biblionumber'} = $biblionumber;
+        my $sth =  $dbh->prepare("select biblioitemnumber from biblioitems where biblionumber=?");
+        $sth->execute($biblionumber);
+        my ($biblioitemnumber) = $sth->fetchrow;
+        $sth->finish(); 
+        $olditem->{'biblioitemnumber'} = $biblioitemnumber;
         _koha_modify_item( $dbh, $olditem );
         return $biblionumber;
     }
@@ -490,9 +439,9 @@ sub ModBiblioframework {
     my ( $biblionumber, $frameworkcode ) = @_;
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare(
-        "UPDATE biblio SET frameworkcode=? WHERE biblionumber=$biblionumber"
+        "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
     );
-    $sth->execute($frameworkcode);
+    $sth->execute($frameworkcode, $biblionumber);
     return 1;
 }
 
@@ -516,14 +465,22 @@ sub ModItemInMarconefield {
 
     my $record = GetMarcItem( $biblionumber, $itemnumber );
     my ($tagfield, $tagsubfield) = GetMarcFromKohaField( $itemfield,'');
-    if ($tagfield && $tagsubfield) {
+    # FIXME - the condition is done this way because GetMarcFromKohaField
+    # returns (0, 0) if it can't field a MARC tag for the kohafield.  However,
+    # some fields like items.wthdrawn are mapped to subfield $0, making the
+    # customary test of "if ($tagfield && $tagsubfield)" incorrect.
+    # GetMarcFromKohaField should probably be returning (undef, undef), making
+    # the correct test "if (defined $tagfield && defined $tagsubfield)", but
+    # this would be a large change and consequently deferred for after 3.0.
+    if (not(int($tagfield) == 0 && int($tagsubfield) == 0)) { 
         my $tag = $record->field($tagfield);
         if ($tag) {
 #             my $tagsubs = $record->field($tagfield)->subfield($tagsubfield);
             $tag->update( $tagsubfield => $newvalue );
             $record->delete_field($tag);
             $record->insert_fields_ordered($tag);
-            &ModItemInMarc( $record, $biblionumber, $itemnumber, 0 );
+            my $frameworkcode = GetFrameworkCode( $biblionumber );
+            &ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
         }
     }
 }
@@ -532,7 +489,7 @@ sub ModItemInMarconefield {
 
 =over
 
-&ModItemInMarc( $record, $biblionumber, $itemnumber )
+&ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode )
 
 =back
 
@@ -614,10 +571,7 @@ sub DelBiblio {
     # - we need to read the biblio if NoZebra is set (to remove it from the indexes
     # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
     #   and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
-    ModZebra($biblionumber, "delete_record", "biblioserver", undef);
-
-    # delete biblio from Koha tables and save in deletedbiblio
-    $error = &_koha_delete_biblio( $dbh, $biblionumber );
+    ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
 
     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
     $sth =
@@ -627,9 +581,16 @@ sub DelBiblio {
     while ( my $biblioitemnumber = $sth->fetchrow ) {
 
         # delete this biblioitem
-        $error = &_koha_delete_biblioitems( $dbh, $biblioitemnumber );
+        $error = _koha_delete_biblioitems( $dbh, $biblioitemnumber );
         return $error if $error;
     }
+
+    # delete biblio from Koha tables and save in deletedbiblio
+    # must do this *after* _koha_delete_biblioitems, otherwise
+    # delete cascade will prevent deletedbiblioitems rows
+    # from being generated by _koha_delete_biblioitems
+    $error = _koha_delete_biblio( $dbh, $biblionumber );
+
     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
         if C4::Context->preference("CataloguingLog");
     return;
@@ -648,7 +609,6 @@ Exported function (core API) for deleting an item record in Koha.
 
 sub DelItem {
     my ( $dbh, $biblionumber, $itemnumber ) = @_;
-    my $dbh = C4::Context->dbh;
        
        # check the item has no current issues
        
@@ -700,14 +660,21 @@ sub GetBiblioData {
     my ( $bibnum ) = @_;
     my $dbh = C4::Context->dbh;
 
-    my $query = "
-        SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
-        FROM biblio
+  #  my $query =  C4::Context->preference('item-level_itypes') ? 
+       #       " SELECT * , biblioitems.notes AS bnotes, biblio.notes
+    #          FROM biblio
+    #        LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
+    #          WHERE biblio.biblionumber = ?
+    #        AND biblioitems.biblionumber = biblio.biblionumber
+    #";
+       
+       my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
+               FROM biblio
             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
-        WHERE biblio.biblionumber = ?
-            AND biblioitems.biblionumber = biblio.biblionumber
-    ";
+               WHERE biblio.biblionumber = ?
+            AND biblioitems.biblionumber = biblio.biblionumber ";
+                
     my $sth = $dbh->prepare($query);
     $sth->execute($bibnum);
     my $data;
@@ -774,25 +741,25 @@ sub GetItemsInfo {
     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
-                 LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
-                WHERE items.biblionumber = ?
-                ORDER BY items.dateaccessioned desc
-                 ";
+                 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" ;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $i = 0;
     my @results;
     my ( $date_due, $count_reserves );
 
+    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"
+       );
     while ( my $data = $sth->fetchrow_hashref ) {
         my $datedue = '';
-        my $isth    = $dbh->prepare(
-            "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname
-            FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
-            WHERE  itemnumber = ?
-                AND returndate IS NULL"
-        );
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
             $data->{borrowernumber} = $idata->{borrowernumber};
@@ -800,13 +767,18 @@ sub GetItemsInfo {
             $data->{surname}     = $idata->{surname};
             $data->{firstname}     = $idata->{firstname};
             $datedue                = format_date( $idata->{'date_due'} );
+           if (C4::Context->preference("IndependantBranches")){
+               my $userenv = C4::Context->userenv;
+               if ( ($userenv) && ( $userenv->{flags} != 1 ) ) { 
+                   $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
+               }
+           }
         }
         if ( $datedue eq '' ) {
             #$datedue="Available";
             my ( $restype, $reserves ) =
               C4::Reserves::CheckReserves( $data->{'itemnumber'} );
             if ($restype) {
-
                 #$datedue=$restype;
                 $count_reserves = $restype;
             }
@@ -1183,13 +1155,14 @@ that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
 sub GetBiblioItemData {
     my ($biblioitemnumber) = @_;
     my $dbh       = C4::Context->dbh;
-    my $sth       =
-      $dbh->prepare(
-       "SELECT *,biblioitems.notes AS bnotes
-               FROM biblioitems,biblio,itemtypes 
-       WHERE biblio.biblionumber = biblioitems.biblionumber 
-               AND biblioitemnumber = ? "
-      );
+       my $query = "SELECT *,biblioitems.notes AS bnotes
+               FROM biblio, biblioitems ";
+       unless(C4::Context->preference('item-level_itypes')) { 
+               $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
+       }        
+       $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber 
+               AND biblioitemnumber = ? ";
+    my $sth       =  $dbh->prepare($query);
     my $data;
     $sth->execute($biblioitemnumber);
     $data = $sth->fetchrow_hashref;
@@ -1249,9 +1222,9 @@ sub GetBiblioItemByBiblioNumber {
 
 =over 4
 
-$item = &GetBiblioFromItemNumber($itemnumber);
+$item = &GetBiblioFromItemNumber($itemnumber,$barcode);
 
-Looks up the item with the given itemnumber.
+Looks up the item with the given itemnumber. if undef, try the barcode.
 
 C<&itemnodata> returns a reference-to-hash whose keys are the fields
 from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
@@ -1263,16 +1236,22 @@ database.
 
 #'
 sub GetBiblioFromItemNumber {
-    my ( $itemnumber ) = @_;
+    my ( $itemnumber, $barcode ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare(
-        "SELECT * FROM items 
-        LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-        LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
-         WHERE items.itemnumber = ?"
-    );
-
-    $sth->execute($itemnumber);
+    my $sth;
+    if($itemnumber) {
+               $sth=$dbh->prepare(  "SELECT * FROM items 
+           LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+           LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+                WHERE items.itemnumber = ?") ; 
+       $sth->execute($itemnumber);
+       } else {
+               $sth=$dbh->prepare(  "SELECT * FROM items 
+           LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+           LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+                WHERE items.barcode = ?") ; 
+       $sth->execute($barcode);
+       }
     my $data = $sth->fetchrow_hashref;
     $sth->finish;
     return ($data);
@@ -1663,11 +1642,14 @@ sub GetMarcBiblio {
      $marcxml =~ s/\x0c//g;  
 #   warn $marcxml;
     my $record = MARC::Record->new();
-     
-      $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour'))} if ($marcxml);
-     if ($@) {warn $@;}
+    if ($marcxml) {
+        $record = eval {MARC::Record::new_from_xml( $marcxml, "utf8", C4::Context->preference('marcflavour'))};
+        if ($@) {warn $@;}
 #      $record = MARC::Record::new_from_usmarc( $marc) if $marc;
-    return $record;
+        return $record;
+    } else {
+        return undef;
+    }
 }
 
 =head2 GetXmlBiblio
@@ -1747,23 +1729,49 @@ Returns MARC::Record of the item passed in parameter.
 
 sub GetMarcItem {
     my ( $biblionumber, $itemnumber ) = @_;
-    my $dbh = C4::Context->dbh;
-    my $newrecord = MARC::Record->new();
-    my $marcflavour = C4::Context->preference('marcflavour');
-    
-    my $marcxml = GetXmlBiblio($biblionumber);
-    my $record = MARC::Record->new();
-    $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
-    # now, find where the itemnumber is stored & extract only the item
-    my ( $itemnumberfield, $itemnumbersubfield ) =
-      GetMarcFromKohaField( 'items.itemnumber', '' );
-    my @fields = $record->field($itemnumberfield);
-    foreach my $field (@fields) {
-        if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
-            $newrecord->insert_fields_ordered($field);
-        }
-    }
-    return $newrecord;
+
+    # GetMarcItem has been revised so that it does the following:
+    #  1. Gets the item information from the items table.
+    #  2. Converts it to a MARC field for storage in the bib record.
+    #
+    # The previous behavior was:
+    #  1. Get the bib record.
+    #  2. Return the MARC tag corresponding to the item record.
+    #
+    # The difference is that one treats the items row as authoritative,
+    # while the other treats the MARC representation as authoritative
+    # under certain circumstances.
+    #
+    # FIXME - a big one
+    #
+    # As of 2007-11-27, this change hopefully does not introduce
+    # any bugs.  However, it does mean that for code that uses
+    # ModItemInMarconefield to update one subfield (corresponding to
+    # an items column) is now less efficient.
+    #
+    # The API needs to be shifted to the following:
+    #  1. User updates items record.
+    #  2. Linked bib is sent for indexing.
+    # 
+    # The missing step 1.5 is updating the item tag in the bib MARC record
+    # so that the indexes are updated.  Depending on performance considerations,
+    # this may ultimately mean of of the following:
+    #  a. MARC field for item is updated right away.
+    #  b. MARC field for item is updated only as part of indexing.
+    #  c. MARC field for item is never actually stored in bib record; instead
+    #     it is generated only when needed for indexing, item export, and
+    #     (maybe) OPAC display.
+    #
+
+    my $itemrecord = GetItem($itemnumber);
+
+    # 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 $itemmarc = TransformKohaToMarc($mungeditem);
+    return $itemmarc;
+
 }
 
 
@@ -1835,48 +1843,49 @@ sub GetMarcSubjects {
         $mintag = "600";
         $maxtag = "611";
     }
+       
+    my @marcsubjects;
+       my $subject = "";
+       my $subfield = "";
+       my $marcsubject;
 
-    my @marcsubjcts;
-
-    foreach my $field ( $record->fields ) {
+    foreach my $field ( $record->field('6..' )) {
         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
+               my @subfields_loop;
         my @subfields = $field->subfields();
-        my $link = "su:";
-        my $label;
-        my $flag = 0;
-        my $authoritysep=C4::Context->preference("authoritysep");
-        for my $subject_subfield ( @subfields ) {
-            if (
-                $marcflavour ne 'MARC21'
-                and (
-                    ($subject_subfield->[0] eq '3') or
-                    ($subject_subfield->[0] eq '4') or
-                    ($subject_subfield->[0] eq '5')
-                )
-            )
-            {
-                next;
-            }
-            my $code = $subject_subfield->[0];
-            $label .= $subject_subfield->[1].$authoritysep unless ( $code == 9 );
-            $link  .= " and su-to:".$subject_subfield->[1]  unless ( $code == 9 );
-            if ( $code == 9 ) {
-                $link = "an:".$subject_subfield->[1];
-                $flag = 1;
-            }
-            elsif ( ! $flag ) {
-                $link =~ s/ and\ssu-to:$//;
+               my $counter = 0;
+               my @link_loop;
+               # if there is an authority link, build the link with an= subfield9
+               my $subfield9 = $field->subfield('9');
+               for my $subject_subfield (@subfields ) {
+                       # don't load unimarc subfields 3,4,5
+                       next if (($marcflavour eq "UNIMARC") and ($subject_subfield->[0] =~ (3|4|5) ) );
+                       my $code = $subject_subfield->[0];
+                       my $value = $subject_subfield->[1];
+                       my $linkvalue = $value;
+                       $linkvalue =~ s/(\(|\))//g;
+                       my $operator = " and " unless $counter==0;
+                       if ($subfield9) {
+                @link_loop = ({'limit' => 'an' ,link => "$subfield9" });
+            } else {
+                push @link_loop, {'limit' => 'su', link => $linkvalue, operator => $operator };
             }
-        }
-         $label =~ s/$authoritysep$//;
-        push @marcsubjcts,
-          {
-            label => $label,
-            link  => $link
-          }
-    }
-    return \@marcsubjcts;
-}    #end GetMarcSubjects
+                       my $separator = C4::Context->preference("authoritysep") unless $counter==0;
+                       # ignore $9
+                       push @subfields_loop, {code => $code, value => $value, link_loop => \@link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
+                       # this needs to be added back in in a way that the template can expose it properly
+                       #if ( $code == 9 ) {
+            #    $link = "an:".$subject_subfield->[1];
+            #    $flag = 1;
+            #}
+                       $counter++;
+               }
+                
+               push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
+        
+       }
+        return \@marcsubjects;
+}  #end getMARCsubjects
 
 =head2 GetMarcAuthors
 
@@ -1900,7 +1909,7 @@ sub GetMarcAuthors {
         $maxtag = "720"; 
     }
     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
-        $mintag = "701";
+        $mintag = "700";
         $maxtag = "712";
     }
        else {
@@ -2407,60 +2416,161 @@ sub TransformHtmlToMarc {
 
 sub TransformMarcToKoha {
     my ( $dbh, $record, $frameworkcode, $table ) = @_;
-    my $result;
 
-       # sometimes we only want to return the items data
-       if ($table eq 'items') {
-           my $sth = $dbh->prepare("SHOW COLUMNS FROM items");
-               $sth->execute();
-       while ( (my $field) = $sth->fetchrow ) {
-               $result = &TransformMarcToKohaOneField( "items", $field, $record, $result, $frameworkcode );
-       }
-               return $result;
-       }
+    my $result;
 
-    my $sth2 = $dbh->prepare("SHOW COLUMNS FROM biblio");
-    $sth2->execute();
-    my $field;
-    while ( ($field) = $sth2->fetchrow ) {
-        $result = &TransformMarcToKohaOneField( "biblio", $field, $record, $result, $frameworkcode );
-    }
-    my $sth2 = $dbh->prepare("SHOW COLUMNS FROM biblioitems");
-    $sth2->execute();
-    while ( ($field) = $sth2->fetchrow ) {
-               if ( $field eq 'notes' ) { $field = 'bnotes'; }
-        $result = &TransformMarcToKohaOneField( "biblioitems", $field, $record, $result, $frameworkcode );
-    }
-    $sth2 = $dbh->prepare("SHOW COLUMNS FROM items");
-    $sth2->execute();
-    while ( ($field) = $sth2->fetchrow ) {
-        $result = &TransformMarcToKohaOneField( "items", $field, $record, $result, $frameworkcode );
+    # sometimes we only want to return the items data
+    if ($table eq 'items') {
+        my $sth = $dbh->prepare("SHOW COLUMNS FROM items");
+        $sth->execute();
+        while ( (my $field) = $sth->fetchrow ) {
+            my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
+            my $key = _disambiguate($table, $field);
+            if ($result->{$key}) {
+                $result->{$key} .= " | " . $value;
+            } else {
+                $result->{$key} = $value;
+            }
+        }
+        return $result;
+    } else {
+        my @tables = ('biblio','biblioitems','items');
+        foreach my $table (@tables){
+            my $sth2 = $dbh->prepare("SHOW COLUMNS from $table");
+            $sth2->execute;
+            while (my ($field) = $sth2->fetchrow){
+                # FIXME use of _disambiguate is a temporary hack
+                # $result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
+                my $value = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
+                my $key = _disambiguate($table, $field);
+                if ($result->{$key}) {
+                    # FIXME - hack to not bring in duplicates of the same value
+                    unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
+                        $result->{$key} .= " | " . $value;
+                    }
+                } else {
+                    $result->{$key} = $value;
+                }
+            }
+            $sth2->finish();
+        }
+        # modify copyrightdate to keep only the 1st year found
+        my $temp = $result->{'copyrightdate'};
+        $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
+        if ( $1 > 0 ) {
+            $result->{'copyrightdate'} = $1;
+        }
+        else {                      # if no cYYYY, get the 1st date.
+            $temp =~ m/(\d\d\d\d)/;
+            $result->{'copyrightdate'} = $1;
+        }
+    
+        # modify publicationyear to keep only the 1st year found
+        $temp = $result->{'publicationyear'};
+        $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
+        if ( $1 > 0 ) {
+            $result->{'publicationyear'} = $1;
+        }
+        else {                      # if no cYYYY, get the 1st date.
+            $temp =~ m/(\d\d\d\d)/;
+            $result->{'publicationyear'} = $1;
+        }
+        return $result;
     }
+}
 
-    # modify copyrightdate to keep only the 1st year found
-    my $temp = $result->{'copyrightdate'};
-    $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
-    if ( $1 > 0 ) {
-        $result->{'copyrightdate'} = $1;
-    }
-    else {                      # if no cYYYY, get the 1st date.
-        $temp =~ m/(\d\d\d\d)/;
-        $result->{'copyrightdate'} = $1;
-    }
 
-    # modify publicationyear to keep only the 1st year found
-    $temp = $result->{'publicationyear'};
-    $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
-    if ( $1 > 0 ) {
-        $result->{'publicationyear'} = $1;
-    }
-    else {                      # if no cYYYY, get the 1st date.
-        $temp =~ m/(\d\d\d\d)/;
-        $result->{'publicationyear'} = $1;
+=head2 _disambiguate
+
+=over 4
+
+$newkey = _disambiguate($table, $field);
+
+This is a temporary hack to distinguish between the
+following sets of columns when using TransformMarcToKoha.
+
+items.cn_source & biblioitems.cn_source
+items.cn_sort & biblioitems.cn_sort
+
+Columns that are currently NOT distinguished (FIXME
+due to lack of time to fully test) are:
+
+biblio.notes and biblioitems.notes
+biblionumber
+timestamp
+biblioitemnumber
+
+FIXME - this is necessary because prefixing each column
+name with the table name would require changing lots
+of code and templates, and exposing more of the DB
+structure than is good to the UI templates, particularly
+since biblio and bibloitems may well merge in a future
+version.  In the future, it would also be good to 
+separate DB access and UI presentation field names
+more.
+
+=back
+
+=cut
+
+sub _disambiguate {
+    my ($table, $column) = @_;
+    if ($column eq "cn_sort" or $column eq "cn_source") {
+        return $table . '.' . $column;
+    } else {
+        return $column;
     }
-    return $result;
+
 }
 
+=head2 get_koha_field_from_marc
+
+=over 4
+
+$result->{_disambiguate($table, $field)} = get_koha_field_from_marc($table,$field,$record,$frameworkcode);
+
+Internal function to map data from the MARC record to a specific non-MARC field.
+FIXME: this is meant to replace TransformMarcToKohaOneField after more testing.
+
+=back
+
+=cut
+
+sub get_koha_field_from_marc {
+    my ($koha_table,$koha_column,$record,$frameworkcode) = @_;
+    my ( $tagfield, $subfield ) = GetMarcFromKohaField( $koha_table.'.'.$koha_column, $frameworkcode );  
+    my $kohafield;
+    foreach my $field ( $record->field($tagfield) ) {
+        if ( $field->tag() < 10 ) {
+            if ( $kohafield ) {
+                $kohafield .= " | " . $field->data();
+            }
+            else {
+                $kohafield = $field->data();
+            }
+        }
+        else {
+            if ( $field->subfields ) {
+                my @subfields = $field->subfields();
+                foreach my $subfieldcount ( 0 .. $#subfields ) {
+                    if ( $subfields[$subfieldcount][0] eq $subfield ) {
+                        if ( $kohafield ) {
+                            $kohafield .=
+                              " | " . $subfields[$subfieldcount][1];
+                        }
+                        else {
+                            $kohafield =
+                              $subfields[$subfieldcount][1];
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return $kohafield;
+} 
+
+
 =head2 TransformMarcToKohaOneField
 
 =over 4
@@ -3015,10 +3125,13 @@ sub GetNoZebraIndexes {
     my %indexes;
     foreach my $line (split /('|"),/,$index) {
         $line =~ /(.*)=>(.*)/;
+warn $line;
         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
         my $fields = $2;
-        $index =~ s/'|"| //g;
-        $fields =~ s/'|"| //g;
+        $index =~ s/'|"|\s//g;
+
+
+        $fields =~ s/'|"|\s//g;
         $indexes{$index}=$fields;
     }
     return %indexes;
@@ -3318,6 +3431,75 @@ sub _find_value {
     return ( $indicator, @result );
 }
 
+=head2 _koha_marc_update_bib_ids
+
+=over 4
+
+_koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
+
+Internal function to add or update biblionumber and biblioitemnumber to
+the MARC XML.
+
+=back
+
+=cut
+
+sub _koha_marc_update_bib_ids {
+    my ($record, $frameworkcode, $biblionumber, $biblioitemnumber) = @_;
+
+    # we must add bibnum and bibitemnum in MARC::Record...
+    # we build the new field with biblionumber and biblioitemnumber
+    # we drop the original field
+    # we add the new builded field.
+    my ($biblio_tag, $biblio_subfield ) = GetMarcFromKohaField("biblio.biblionumber",$frameworkcode);
+    my ($biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField("biblioitems.biblioitemnumber",$frameworkcode);
+
+    if ($biblio_tag != $biblioitem_tag) {
+        # biblionumber & biblioitemnumber are in different fields
+
+        # deal with biblionumber
+        my ($new_field, $old_field);
+        if ($biblio_tag < 10) {
+            $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
+        } else {
+            $new_field =
+              MARC::Field->new( $biblio_tag, '', '',
+                "$biblio_subfield" => $biblionumber );
+        }
+
+        # drop old field and create new one...
+        $old_field = $record->field($biblio_tag);
+        $record->delete_field($old_field);
+        $record->append_fields($new_field);
+
+        # deal with biblioitemnumber
+        if ($biblioitem_tag < 10) {
+            $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
+        } else {
+            $new_field =
+              MARC::Field->new( $biblioitem_tag, '', '',
+                "$biblioitem_subfield" => $biblioitemnumber, );
+        }
+        # drop old field and create new one...
+        $old_field = $record->field($biblioitem_tag);
+        $record->delete_field($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);
+        $record->insert_fields_ordered($new_field);
+    }
+}
+
 =head2 _koha_add_biblio
 
 =over 4
@@ -3335,20 +3517,13 @@ sub _koha_add_biblio {
 
        my $error;
 
-       # get the next biblionumber
-    my $sth = $dbh->prepare("SELECT MAX(biblionumber) FROM biblio");
-    $sth->execute();
-    my $data = $sth->fetchrow_arrayref();
-    my $biblionumber = $$data[0] + 1;
        # set the series flag
     my $serial = 0;
     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
 
-    $sth->finish();
        my $query = 
         "INSERT INTO biblio
-               SET biblionumber  = ?, 
-                       frameworkcode = ?,
+               SET frameworkcode = ?,
                        author = ?,
                        title = ?,
                        unititle =?,
@@ -3359,9 +3534,8 @@ sub _koha_add_biblio {
                        datecreated=NOW(),
                        abstract = ?
                ";
-    $sth = $dbh->prepare($query);
+    my $sth = $dbh->prepare($query);
     $sth->execute(
-        $biblionumber,
                $frameworkcode,
         $biblio->{'author'},
         $biblio->{'title'},
@@ -3373,6 +3547,7 @@ sub _koha_add_biblio {
         $biblio->{'abstract'}
     );
 
+    my $biblionumber = $dbh->{'mysql_insertid'};
        if ( $dbh->errstr ) {
                $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
         warn $error;
@@ -3387,7 +3562,7 @@ sub _koha_add_biblio {
 
 =over 4
 
-my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio);
+my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
 
 Internal function for updating the biblio table
 
@@ -3396,7 +3571,7 @@ Internal function for updating the biblio table
 =cut
 
 sub _koha_modify_biblio {
-    my ( $dbh, $biblio ) = @_;
+    my ( $dbh, $biblio, $frameworkcode ) = @_;
        my $error;
 
     my $query = "
@@ -3416,7 +3591,7 @@ sub _koha_modify_biblio {
     my $sth = $dbh->prepare($query);
     
     $sth->execute(
-               $biblio->{'frameworkcode'},
+               $frameworkcode,
         $biblio->{'author'},
         $biblio->{'title'},
         $biblio->{'unititle'},
@@ -3435,22 +3610,25 @@ sub _koha_modify_biblio {
     return ( $biblio->{'biblionumber'},$error );
 }
 
-=head2 _koha_modify_biblioitem
+=head2 _koha_modify_biblioitem_nonmarc
 
 =over 4
 
-my ($biblioitemnumber,$error) = _koha_modify_biblioitem( $dbh, $biblioitem );
+my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
+
+Updates biblioitems row except for marc and marcxml, which should be changed
+via ModBiblioMarc
 
 =back
 
 =cut
 
-sub _koha_modify_biblioitem {
+sub _koha_modify_biblioitem_nonmarc {
     my ( $dbh, $biblioitem ) = @_;
        my $error;
 
        # re-calculate the cn_sort, it may have changed
-       my ($cn_sort) = GetClassSort($biblioitem->{'cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+       my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
 
        my $query = 
        "UPDATE biblioitems 
@@ -3475,15 +3653,13 @@ sub _koha_modify_biblioitem {
                size                    = ?,
                place                   = ?,
                lccn                    = ?,
-               marc                    = ?,
                url                     = ?,
         cn_source              = ?,
         cn_class        = ?,
         cn_item                = ?,
                cn_suffix       = ?,
                cn_sort         = ?,
-               totalissues     = ?,
-               marcxml         = ?
+               totalissues     = ?
         where biblioitemnumber = ?
                ";
        my $sth = $dbh->prepare($query);
@@ -3509,19 +3685,17 @@ sub _koha_modify_biblioitem {
                $biblioitem->{'size'},
                $biblioitem->{'place'},
                $biblioitem->{'lccn'},
-               $biblioitem->{'marc'},
                $biblioitem->{'url'},
-               $biblioitem->{'cn_source'},
+               $biblioitem->{'biblioitems.cn_source'},
                $biblioitem->{'cn_class'},
                $biblioitem->{'cn_item'},
                $biblioitem->{'cn_suffix'},
                $cn_sort,
                $biblioitem->{'totalissues'},
-               $biblioitem->{'marcxml'},
                $biblioitem->{'biblioitemnumber'}
        );
     if ( $dbh->errstr ) {
-               $error.="ERROR in _koha_modify_biblioitem $query".$dbh->errstr;
+               $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
         warn $error;
     }
        return ($biblioitem->{'biblioitemnumber'},$error);
@@ -3542,16 +3716,10 @@ Internal function to add a biblioitem
 sub _koha_add_biblioitem {
     my ( $dbh, $biblioitem ) = @_;
        my $error;
-    my $sth = $dbh->prepare("SELECT MAX(biblioitemnumber) FROM biblioitems");
-    $sth->execute();
-    my $data       = $sth->fetchrow_arrayref;
-    my $bibitemnum = $$data[0] + 1;
-    $sth->finish();
 
-       my ($cn_sort) = GetClassSort($biblioitem->{'cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+       my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
     my $query =
     "INSERT INTO biblioitems SET
-               biblioitemnumber = ?,
         biblionumber    = ?,
         volume          = ?,
         number          = ?,
@@ -3584,7 +3752,6 @@ sub _koha_add_biblioitem {
         ";
        my $sth = $dbh->prepare($query);
     $sth->execute(
-               $bibitemnum,
         $biblioitem->{'biblionumber'},
         $biblioitem->{'volume'},
         $biblioitem->{'number'},
@@ -3608,13 +3775,14 @@ sub _koha_add_biblioitem {
         $biblioitem->{'lccn'},
         $biblioitem->{'marc'},
         $biblioitem->{'url'},
-        $biblioitem->{'cn_source'},
+        $biblioitem->{'biblioitems.cn_source'},
         $biblioitem->{'cn_class'},
         $biblioitem->{'cn_item'},
         $biblioitem->{'cn_suffix'},
         $cn_sort,
         $biblioitem->{'totalissues'}
     );
+    my $bibitemnum = $dbh->{'mysql_insertid'};
     if ( $dbh->errstr ) {
                $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
                warn $error;
@@ -3637,13 +3805,7 @@ sub _koha_new_items {
     my ( $dbh, $item, $barcode ) = @_;
        my $error;
 
-    my $sth = $dbh->prepare("SELECT MAX(itemnumber) FROM items");
-    $sth->execute();
-    my $data       = $sth->fetchrow_hashref;
-    my $itemnumber = $data->{'MAX(itemnumber)'} + 1;
-    $sth->finish;
-
-    my ($items_cn_sort) = GetClassSort($item->{'cn_source'}, $item->{'itemcallnumber'}, "");
+    my ($items_cn_sort) = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
 
     # if dateaccessioned is provided, use it. Otherwise, set to NOW()
     if ( $item->{'dateaccessioned'} eq '' || !$item->{'dateaccessioned'} ) {
@@ -3652,7 +3814,6 @@ sub _koha_new_items {
        }
        my $query = 
            "INSERT INTO items SET
-            itemnumber                 = ?,
                        biblionumber            = ?,
             biblioitemnumber    = ?,
                        barcode                 = ?,
@@ -3676,15 +3837,18 @@ sub _koha_new_items {
             paidfor            = ?,
                        location                        = ?,
                        onloan                          = ?,
+                       issues                          = ?,
+                       renewals                        = ?,
+                       reserves                        = ?,
                        cn_source                       = ?,
                        cn_sort                         = ?,
                        ccode                           = ?,
+                       itype                           = ?,
                        materials                       = ?,
                        uri                             = ?
           ";
     my $sth = $dbh->prepare($query);
        $sth->execute(
-            $itemnumber,
                        $item->{'biblionumber'},
                        $item->{'biblioitemnumber'},
             $barcode,
@@ -3706,12 +3870,17 @@ sub _koha_new_items {
                        $item->{'paidfor'},
                        $item->{'location'},
                        $item->{'onloan'},
-                       $item->{'cn_source'},
+                       $item->{'issues'},
+                       $item->{'renewals'},
+                       $item->{'reserves'},
+                       $item->{'items.cn_source'},
                        $items_cn_sort,
                        $item->{'ccode'},
+                       $item->{'itype'},
                        $item->{'materials'},
                        $item->{'uri'},
     );
+    my $itemnumber = $dbh->{'mysql_insertid'};
     if ( defined $sth->errstr ) {
         $error.="ERROR in _koha_new_items $query".$sth->errstr;
     }
@@ -3733,22 +3902,14 @@ sub _koha_modify_item {
     my ( $dbh, $item ) = @_;
        my $error;
 
-       # calculate items_cn_sort
-    my ($items_cn_sort) = GetClassSort($item->{'cn_source'}, $item->{'itemcallnumber'}, "");
+       # calculate items.cn_sort
+    $item->{'cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
 
     my $query = "UPDATE items SET ";
        my @bind;
        for my $key ( keys %$item ) {
-               # special cases first
-               if ($key eq 'cn_sort') {
-                       $query.="cn_sort=?,";
-                       push @bind, $items_cn_sort;
-               }
-               # now all the rest
-               else {
-                       $query.="$key=?,";
-                       push @bind, $item->{$key};
-               }
+               $query.="$key=?,";
+               push @bind, $item->{$key};
     }
        $query =~ s/,$//;
     $query .= " WHERE itemnumber=?";