only munge copyrightdate and publicationyear if they exist
[koha.git] / C4 / Biblio.pm
old mode 100644 (file)
new mode 100755 (executable)
index c990a2f..1b9c02f
@@ -20,27 +20,29 @@ package C4::Biblio;
 use strict;
 
 require Exporter;
+# use utf8;
 use C4::Context;
 use MARC::Record;
 use MARC::File::USMARC;
 use MARC::File::XML;
 use ZOOM;
 use C4::Koha;
-use C4::Date;
-use utf8;
+use C4::Branch;
+use C4::Dates qw/format_date/;
 use C4::Log; # logaction
+use C4::ClassSource;
 
 use vars qw($VERSION @ISA @EXPORT);
 
-# set the version for version checking
-$VERSION = do { my @v = '$Revision$' =~ /\d+/g; shift(@v).".".join( "_", map { sprintf "%03d", $_ } @v ); };
+# TODO: fix version
+# $VERSION = ?;
 
 @ISA = qw( Exporter );
 
 # EXPORTED FUNCTIONS.
 
 # to add biblios or items
-push @EXPORT, qw( &AddBiblio &AddItem );
+push @EXPORT, qw( &AddBiblio &AddItem &AddBiblioAndItems );
 
 # to get something
 push @EXPORT, qw(
@@ -65,8 +67,11 @@ push @EXPORT, qw(
   &GetMarcBiblio
   &GetMarcAuthors
   &GetMarcSeries
+  GetMarcUrls
+  &GetUsedMarcStructure
 
   &GetItemsInfo
+  &GetItemsByBiblioitemnumber
   &GetItemnumberFromBarcode
   &get_itemnumbers_of
   &GetXmlBiblio
@@ -75,6 +80,7 @@ push @EXPORT, qw(
   &GetMarcStructure
   &GetMarcFromKohaField
   &GetFrameworkCode
+  &GetPublisherNameFromIsbn
   &TransformKohaToMarc
 );
 
@@ -103,13 +109,12 @@ push @EXPORT, qw(
 push @EXPORT, qw(
   &ModBiblioMarc
   &AddItemInMarc
-  &calculatelc
-  &itemcalculator
 );
 
 # Others functions
 push @EXPORT, qw(
   &TransformMarcToKoha
+  &TransformHtmlToMarc2
   &TransformHtmlToMarc
   &TransformHtmlToXml
   &PrepareItemrecordDisplay
@@ -204,86 +209,211 @@ Exported function (core API) for adding a new biblio to koha.
 
 sub AddBiblio {
     my ( $record, $frameworkcode ) = @_;
-    my $biblionumber;
-    my $biblioitemnumber;
+    my ($biblionumber,$biblioitemnumber,$error);
     my $dbh = C4::Context->dbh;
     # transform the data into koha-table style data
     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
-    $biblionumber = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
+    ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
     $olddata->{'biblionumber'} = $biblionumber;
-    $biblioitemnumber = _koha_add_biblioitem( $dbh, $olddata );
+    ($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);
+    _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
+
+    # now add the record
+    $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
+      
+    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
+        if C4::Context->preference("CataloguingLog");
+
+    return ( $biblionumber, $biblioitemnumber );
+}
+
+=head2 AddBiblioAndItems
 
-    my $newfield;
+=over 4
 
-    # biblionumber & biblioitemnumber are in different fields
-    if ( $biblio_tag != $biblioitem_tag ) {
+($biblionumber,$biblioitemnumber, $itemnumber_ref, $error_ref) = AddBiblioAndItems($record, $frameworkcode);
 
-        # deal with biblionumber
-        if ( $biblio_tag < 10 ) {
-            $newfield = MARC::Field->new( $biblio_tag, $biblionumber );
+=back
+
+Efficiently add a biblio record and create item records from its
+embedded item fields.  This routine is suitable for batch jobs.
+
+The goal of this API is to have a similar effect to using AddBiblio
+and AddItems in succession, but without inefficient repeated
+parsing of the MARC XML bib record.
+
+One functional difference is that the duplicate item barcode 
+check is implemented in this API, instead of relying on
+the caller to do it, like AddItem does.
+
+This function returns the biblionumber and biblioitemnumber of the
+new bib, an arrayref of new itemsnumbers, and an arrayref of item
+errors encountered during the processing.  Each entry in the errors
+list is a hashref containing the following keys:
+
+=over 2
+
+=item item_sequence
+
+Sequence number of original item tag in the MARC record.
+
+=item item_barcode
+
+Item barcode, provide to assist in the construction of
+useful error messages.
+
+=item error_condition
+
+Code representing the error condition.  Can be 'duplicate_barcode',
+'invalid_homebranch', or 'invalid_holdingbranch'.
+
+=item error_information
+
+Additional information appropriate to the error condition.
+
+=back
+
+=cut
+
+sub AddBiblioAndItems {
+    my ( $record, $frameworkcode ) = @_;
+    my ($biblionumber,$biblioitemnumber,$error);
+    my @itemnumbers = ();
+    my @errors = ();
+    my $dbh = C4::Context->dbh;
+
+    # transform the data into koha-table style data
+    # FIXME - this paragraph copied from AddBiblio
+    my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
+    ($biblionumber,$error) = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
+    $olddata->{'biblionumber'} = $biblionumber;
+    ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $olddata );
+
+    # FIXME - this paragraph copied from AddBiblio
+    _koha_marc_update_bib_ids($record, $frameworkcode, $biblionumber, $biblioitemnumber);
+
+    # now we loop through the item tags and start creating items
+    my @bad_item_fields = ();
+    my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
+    my $item_sequence_num = 0;
+    ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
+        $item_sequence_num++;
+        # we take the item field and stick it into a new
+        # MARC record -- this is required so far because (FIXME)
+        # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
+        # and there is no TransformMarcFieldToKoha
+        my $temp_item_marc = MARC::Record->new();
+        $temp_item_marc->append_fields($item_field);
+    
+        # add biblionumber and biblioitemnumber
+        my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
+        $item->{'biblionumber'} = $biblionumber;
+        $item->{'biblioitemnumber'} = $biblioitemnumber;
+
+        # check for duplicate barcode
+        my %item_errors = CheckItemPreSave($item);
+        if (%item_errors) {
+            push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
+            push @bad_item_fields, $item_field;
+            next ITEMFIELD;
         }
-        else {
-            $newfield =
-              MARC::Field->new( $biblio_tag, '', '',
-                "$biblio_subfield" => $biblionumber );
+        my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
+        if ($duplicate_barcode) {
+            warn "ERROR: cannot add item $item->{'barcode'} for biblio $biblionumber: duplicate barcode\n";
         }
 
-        # drop old field and create new one...
-        my $old_field = $record->field($biblio_tag);
-        $record->delete_field($old_field);
-        $record->append_fields($newfield);
+        # figure out what item type to use -- biblioitem-level or item-level
+        my $itemtype;
+        if (C4::Context->preference('item-level_itypes')) {
+            $itemtype = $item->{'itype'};
+        } else {
+            $itemtype = $olddata->{'itemtype'};
+        }
 
-        # deal with biblioitemnumber
-        if ( $biblioitem_tag < 10 ) {
-            $newfield = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
+        # FIXME - notforloan stuff copied from AddItem
+        my $sth = $dbh->prepare("SELECT notforloan FROM itemtypes WHERE itemtype=?");
+        $sth->execute($itemtype);
+        my $notforloan = $sth->fetchrow;
+        ##Change the notforloan field if $notforloan found
+        if ( $notforloan > 0 ) {
+            $item->{'notforloan'} = $notforloan;
+            &MARCitemchange( $temp_item_marc, "items.notforloan", $notforloan );
         }
-        else {
-            $newfield =
-              MARC::Field->new( $biblioitem_tag, '', '',
-                "$biblioitem_subfield" => $biblioitemnumber, );
+
+        # FIXME - dateaccessioned stuff copied from AddItem
+        if ( !$item->{'dateaccessioned'} || $item->{'dateaccessioned'} eq '' ) {
+
+            # find today's date
+            my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
+                localtime(time);
+            $year += 1900;
+            $mon  += 1;
+            my $date =
+            "$year-" . sprintf( "%0.2d", $mon ) . "-" . sprintf( "%0.2d", $mday );
+            $item->{'dateaccessioned'} = $date;
+            &MARCitemchange( $temp_item_marc, "items.dateaccessioned", $date );
         }
-        # 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)
+        my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
+        warn $error if $error;
+        push @itemnumbers, $itemnumber; # FIXME not checking error
+
+        # FIXME - not copied from AddItem
+        # FIXME - AddItems equiv code about passing $sth to TransformKohaToMarcOneField is stupid
+        &MARCitemchange( $temp_item_marc, "items.itemnumber", $itemnumber );
+       
+        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
+        if C4::Context->preference("CataloguingLog"); 
+
+        $item_field->replace_with($temp_item_marc->field($itemtag));
     }
-    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);
+    # remove any MARC item fields for rejected items
+    foreach my $item_field (@bad_item_fields) {
+        $record->delete_field($item_field);
     }
 
     # now add the record
+    # FIXME - this paragraph copied from AddBiblio -- however, moved  since
+    # since we need to create the items row and plug in the itemnumbers in the
+    # MARC
     $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
-      
-    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
+
+    # FIXME - when using this API, do we log both bib and item add, or just
+    #         bib
+    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio")
         if C4::Context->preference("CataloguingLog");
-      
-    return ( $biblionumber, $biblioitemnumber );
+
+    return ( $biblionumber, $biblioitemnumber, \@itemnumbers, \@errors);
+    
+}
+
+sub _repack_item_errors {
+    my $item_sequence_num = shift;
+    my $item_ref = shift;
+    my $error_ref = shift;
+
+    my @repacked_errors = ();
+
+    foreach my $error_code (sort keys %{ $error_ref }) {
+        my $repacked_error = {};
+        $repacked_error->{'item_sequence'} = $item_sequence_num;
+        $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
+        $repacked_error->{'error_code'} = $error_code;
+        $repacked_error->{'error_information'} = $error_ref->{$error_code};
+        push @repacked_errors, $repacked_error;
+    } 
+
+    return @repacked_errors;
 }
 
 =head2 AddItem
 
-=over
+=over 2
 
-$biblionumber = AddItem( $record, $biblionumber)
-Exported function (core API) for adding a new item to Koha
+    $biblionumber = AddItem( $record, $biblionumber)
+    Exported function (core API) for adding a new item to Koha
 
 =back
 
@@ -301,15 +431,15 @@ sub AddItem {
     $item->{'biblionumber'} = $biblionumber;
     my $sth =
       $dbh->prepare(
-        "select biblioitemnumber,itemtype from biblioitems where biblionumber=?"
+        "SELECT biblioitemnumber,itemtype FROM biblioitems WHERE biblionumber=?"
       );
     $sth->execute( $item->{'biblionumber'} );
     my $itemtype;
     ( $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 ) {
@@ -328,20 +458,20 @@ sub AddItem {
         $item->{'dateaccessioned'} = $date;
         &MARCitemchange( $record, "items.dateaccessioned", $date );
     }
-    my ( $itemnumber, $error ) =
-      &_koha_new_items( $dbh, $item, $item->{barcode} );
-
+    my ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, $item->{barcode} );
     # add itemnumber to MARC::Record before adding the item.
-    $sth =
-      $dbh->prepare(
-"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
+    $sth = $dbh->prepare(
+"SELECT tagfield,tagsubfield 
+FROM marc_subfield_structure
+WHERE frameworkcode=? 
+    AND kohafield=?"
       );
     &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
         $frameworkcode );
 
     # add the item
     &AddItemInMarc( $record, $item->{'biblionumber'},$frameworkcode );
-    
+   
     &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
         if C4::Context->preference("CataloguingLog");
     
@@ -350,21 +480,16 @@ sub AddItem {
 
 =head2 ModBiblio
 
-=over
-
-ModBiblio( $record,$biblionumber,$frameworkcode);
-Exported function (core API) to modify a biblio
-
-=back
+    ModBiblio( $record,$biblionumber,$frameworkcode);
+    Exported function (core API) to modify a biblio
 
 =cut
 
 sub ModBiblio {
     my ( $record, $biblionumber, $frameworkcode ) = @_;
-    
-    if (C4::Context->preference("CataloguingLog")) {    
+    if (C4::Context->preference("CataloguingLog")) {
         my $newrecord = GetMarcBiblio($biblionumber);
-        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,$newrecord->as_formatted) 
+        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,"BEFORE=>".$newrecord->as_formatted);
     }
     
     my $dbh = C4::Context->dbh;
@@ -373,26 +498,50 @@ sub ModBiblio {
 
     # 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);
-    my @fields = $oldRecord->field($itemtag);
-    $record->append_fields(@fields);
+    my $oldRecord = GetMarcBiblio( $biblionumber );
+    
+    # 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()) {
+            if ($field) {
+                $field->add_subfields(Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
+            } else {
+                $field = MARC::Field->new("$itemtag",'','',Encode::encode('utf-8',$_->[0]) => Encode::encode('utf-8',$_->[1]));
+            }
+          }
+        $record->append_fields($field);
+    }
+    
+    # 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 );
-
+    &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
+    
     # load the koha-table data object
     my $oldbiblio = TransformMarcToKoha( $dbh, $record, $frameworkcode );
 
     # modify the other koha tables
-    $biblionumber = _koha_modify_biblio( $dbh, $oldbiblio );
-    _koha_modify_biblioitem( $dbh, $oldbiblio );
-
+    _koha_modify_biblio( $dbh, $oldbiblio, $frameworkcode );
+    _koha_modify_biblioitem_nonmarc( $dbh, $oldbiblio );
     return 1;
 }
 
 =head2 ModItem
 
-=over
+=over 2
 
 Exported function (core API) for modifying an item in Koha.
 
@@ -416,7 +565,13 @@ sub ModItem {
     if ($record) {
         my $frameworkcode = GetFrameworkCode( $biblionumber );
         ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode );
-        my $olditem       = TransformMarcToKoha( $dbh, $record, $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;
     }
@@ -436,11 +591,11 @@ sub ModItemTransfer {
     #new entry in branchtransfers....
     my $sth = $dbh->prepare(
         "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
-        VALUES (?, ?, now(), ?)");
+        VALUES (?, ?, NOW(), ?)");
     $sth->execute($itemnumber, $frombranch, $tobranch);
     #update holdingbranch in items .....
      $sth= $dbh->prepare(
-          "UPDATE items set holdingbranch = ? WHERE items.itemnumber = ?");
+          "UPDATE items SET holdingbranch = ? WHERE items.itemnumber = ?");
     $sth->execute($tobranch,$itemnumber);
     &ModDateLastSeen($itemnumber);
     $sth = $dbh->prepare(
@@ -454,39 +609,20 @@ sub ModItemTransfer {
     return;
 }
 
-##New sub to dotransfer in marc tables as well. Not exported -TG 10/04/2006
-# sub domarctransfer {
-#     my ( $dbh, $itemnumber ) = @_;
-#     $itemnumber =~ s /\'//g;    ##itemnumber seems to come with quotes-TG
-#     my $sth =
-#       $dbh->prepare(
-#         "select biblionumber,holdingbranch from items where itemnumber=$itemnumber"
-#       );
-#     $sth->execute();
-#     while ( my ( $biblionumber, $holdingbranch ) = $sth->fetchrow ) {
-#         &ModItemInMarconefield( $biblionumber, $itemnumber,
-#             'items.holdingbranch', $holdingbranch );
-#     }
-#     return;
-# }
 =head2 ModBiblioframework
 
-=over
-
-ModBiblioframework($biblionumber,$frameworkcode);
-Exported function to modify a biblio framework
-
-=back
+    ModBiblioframework($biblionumber,$frameworkcode);
+    Exported function to modify a biblio framework
 
 =cut
 
 sub ModBiblioframework {
     my ( $biblionumber, $frameworkcode ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth =
-      $dbh->prepare(
-        "UPDATE biblio SET frameworkcode=? WHERE biblionumber=$biblionumber");
-    $sth->execute($frameworkcode);
+    my $sth = $dbh->prepare(
+        "UPDATE biblio SET frameworkcode=? WHERE biblionumber=?"
+    );
+    $sth->execute($frameworkcode, $biblionumber);
     return 1;
 }
 
@@ -510,14 +646,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 );
         }
     }
 }
@@ -526,7 +670,7 @@ sub ModItemInMarconefield {
 
 =over
 
-&ModItemInMarc( $record, $biblionumber, $itemnumber )
+&ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode )
 
 =back
 
@@ -548,7 +692,7 @@ sub ModItemInMarc {
         }
     }
     # save the record
-    my $sth = $dbh->prepare("update biblioitems set marc=?,marcxml=?  where biblionumber=?");
+    my $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
     $sth->execute( $completeRecord->as_usmarc(), $completeRecord->as_xml_record(),$biblionumber );
     $sth->finish;
     ModZebra($biblionumber,"specialUpdate","biblioserver",$completeRecord);
@@ -567,7 +711,7 @@ sub ModDateLastSeen {
     my $dbh       = C4::Context->dbh;
     my $sth       =
       $dbh->prepare(
-          "update items set itemlost=0, datelastseen  = now() where items.itemnumber = ?"
+          "UPDATE items SET itemlost=0,datelastseen  = NOW() WHERE items.itemnumber = ?"
       );
     $sth->execute($itemnum);
     return;
@@ -592,26 +736,15 @@ sub DelBiblio {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
     my $error;    # for error handling
-
-    # First make sure there are no items with issues are still attached
-    my $sth =
-      $dbh->prepare(
-        "SELECT itemnumber FROM items WHERE biblionumber=?");
+    
+    # First make sure this biblio has no items attached
+    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
     $sth->execute($biblionumber);
-    while ( my $itemnumber = $sth->fetchrow ) {
-        my $issues = GetItemIssues($itemnumber);
-        foreach my $issue (@$issues) {
-            if (   ( $issue->{date_due} )
-                && ( $issue->{date_due} ne "Available" ) )
-            {
-
-#FIXME: we need a status system in Biblio like in Circ to return standard codes and messages
-# instead of hard-coded strings
-                $error .=
-"Item is checked out to a patron -- you must return it before deleting the Biblio";
-            }
-        }
+    if (my $itemnumber = $sth->fetchrow){
+        # Fix this to use a status the template can understand
+        $error .= "This Biblio has items attached, please delete them first before deleting this biblio ";
     }
+
     return $error if $error;
 
     # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
@@ -619,10 +752,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 =
@@ -632,19 +762,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 items
-        my $items_sth =
-          $dbh->prepare(
-            "SELECT itemnumber FROM items WHERE biblioitemnumber=?");
-        $items_sth->execute($biblioitemnumber);
-        while ( my $itemnumber = $items_sth->fetchrow ) {
-            $error = &_koha_delete_item( $dbh, $itemnumber );
-            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;
@@ -662,21 +789,25 @@ Exported function (core API) for deleting an item record in Koha.
 =cut
 
 sub DelItem {
-    my ( $biblionumber, $itemnumber ) = @_;
-    my $dbh = C4::Context->dbh;
+    my ( $dbh, $biblionumber, $itemnumber ) = @_;
+    
+    # check the item has no current issues
+    
+    
     &_koha_delete_item( $dbh, $itemnumber );
+
     # get the MARC record
     my $record = GetMarcBiblio($biblionumber);
     my $frameworkcode = GetFrameworkCode($biblionumber);
 
     # backup the record
-    my $copy2deleted =
-      $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
+    my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
     $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
 
     #search item field code
     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
     my @fields = $record->field($itemtag);
+
     # delete the item specified
     foreach my $field (@fields) {
         if ( $field->subfield($itemsubfield) eq $itemnumber ) {
@@ -688,6 +819,92 @@ sub DelItem {
         if C4::Context->preference("CataloguingLog");
 }
 
+=head2 CheckItemPreSave
+
+=over 4
+
+    my $item_ref = TransformMarcToKoha($marc, 'items');
+    # do stuff
+    my %errors = CheckItemPreSave($item_ref);
+    if (exists $errors{'duplicate_barcode'}) {
+        print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
+    } elsif (exists $errors{'invalid_homebranch'}) {
+        print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
+    } elsif (exists $errors{'invalid_holdingbranch'}) {
+        print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
+    } else {
+        print "item is OK";
+    }
+
+=back
+
+Given a hashref containing item fields, determine if it can be
+inserted or updated in the database.  Specifically, checks for
+database integrity issues, and returns a hash containing any
+of the following keys, if applicable.
+
+=over 2
+
+=item duplicate_barcode
+
+Barcode, if it duplicates one already found in the database.
+
+=item invalid_homebranch
+
+Home branch, if not defined in branches table.
+
+=item invalid_holdingbranch
+
+Holding branch, if not defined in branches table.
+
+=back
+
+This function does NOT implement any policy-related checks,
+e.g., whether current operator is allowed to save an
+item that has a given branch code.
+
+=cut
+
+sub CheckItemPreSave {
+    my $item_ref = shift;
+
+    my %errors = ();
+
+    # check for duplicate barcode
+    if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
+        my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
+        if ($existing_itemnumber) {
+            if (!exists $item_ref->{'itemnumber'}                       # new item
+                or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
+                $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
+            }
+        }
+    }
+
+    # check for valid home branch
+    if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
+        my $branch_name = GetBranchName($item_ref->{'homebranch'});
+        unless (defined $branch_name) {
+            # relies on fact that branches.branchname is a non-NULL column,
+            # so GetBranchName returns undef only if branch does not exist
+            $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
+        }
+    }
+
+    # check for valid holding branch
+    if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
+        my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
+        unless (defined $branch_name) {
+            # relies on fact that branches.branchname is a non-NULL column,
+            # so GetBranchName returns undef only if branch does not exist
+            $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
+        }
+    }
+
+    return %errors;
+
+}
+
 =head2 GetBiblioData
 
 =over 4
@@ -710,14 +927,21 @@ sub GetBiblioData {
     my ( $bibnum ) = @_;
     my $dbh = C4::Context->dbh;
 
-    my $query = "
-        SELECT * , biblioitems.notes AS bnotes, 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;
@@ -782,41 +1006,45 @@ sub GetItemsInfo {
     my ( $biblionumber, $type ) = @_;
     my $dbh   = C4::Context->dbh;
     my $query = "SELECT *,items.notforloan as itemnotforloan
-                 FROM items, biblio, biblioitems
-                 LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
-                WHERE items.biblionumber = ?
-                    AND biblioitems.biblioitemnumber = items.biblioitemnumber
-                    AND biblio.biblionumber = items.biblionumber
-                ORDER BY items.dateaccessioned desc
-                 ";
+                 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" ;
     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
-            FROM   issues, borrowers
-            WHERE  itemnumber = ?
-                AND returndate IS NULL
-                AND issues.borrowernumber=borrowers.borrowernumber"
-        );
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
             $data->{borrowernumber} = $idata->{borrowernumber};
             $data->{cardnumber}     = $idata->{cardnumber};
-            $datedue                = format_date( $idata->{'date_due'} );
+            $data->{surname}     = $idata->{surname};
+            $data->{firstname}     = $idata->{firstname};
+            $datedue                = $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;
             }
         }
@@ -831,8 +1059,6 @@ sub GetItemsInfo {
         if ( my $bdata = $bsth->fetchrow_hashref ) {
             $data->{'branchname'} = $bdata->{'branchname'};
         }
-        my $date = format_date( $data->{'datelastseen'} );
-        $data->{'datelastseen'}   = $date;
         $data->{'datedue'}        = $datedue;
         $data->{'count_reserves'} = $count_reserves;
 
@@ -880,6 +1106,20 @@ sub GetItemsInfo {
             my ($lib) = $stackstatus->fetchrow;
             $data->{stack} = $lib;
         }
+        # Find the last 3 people who borrowed this item.
+        my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
+                                    WHERE itemnumber = ?
+                                    AND issues.borrowernumber = borrowers.borrowernumber
+                                    AND returndate IS NOT NULL LIMIT 3");
+        $sth2->execute($data->{'itemnumber'});
+        my $ii = 0;
+        while (my $data2 = $sth2->fetchrow_hashref()) {
+            $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
+            $data->{"card$ii"}      = $data2->{'cardnumber'} if $data2->{'cardnumber'};
+            $data->{"borrower$ii"}  = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
+            $ii++;
+        }
+
         $results[$i] = $data;
         $i++;
     }
@@ -936,13 +1176,22 @@ sub GetItemStatus {
     if ( $tag and $subfield ) {
         my $sth =
           $dbh->prepare(
-"select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
+            "SELECT authorised_value
+            FROM marc_subfield_structure
+            WHERE tagfield=?
+                AND tagsubfield=?
+                AND frameworkcode=?
+            "
           );
         $sth->execute( $tag, $subfield, $fwk );
         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
             my $authvalsth =
               $dbh->prepare(
-"select authorised_value, lib from authorised_values where category=? order by lib"
+                "SELECT authorised_value,lib
+                FROM authorised_values 
+                WHERE category=? 
+                ORDER BY lib
+                "
               );
             $authvalsth->execute($authorisedvaluecat);
             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
@@ -1015,13 +1264,20 @@ sub GetItemLocation {
     if ( $tag and $subfield ) {
         my $sth =
           $dbh->prepare(
-"select authorised_value from marc_subfield_structure where tagfield=? and tagsubfield=? and frameworkcode=?"
+            "SELECT authorised_value
+            FROM marc_subfield_structure 
+            WHERE tagfield=? 
+                AND tagsubfield=? 
+                AND frameworkcode=?"
           );
         $sth->execute( $tag, $subfield, $fwk );
         if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
             my $authvalsth =
               $dbh->prepare(
-"select authorised_value, lib from authorised_values where category=? order by lib"
+                "SELECT authorised_value,lib
+                FROM authorised_values
+                WHERE category=?
+                ORDER BY lib"
               );
             $authvalsth->execute($authorisedvaluecat);
             while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
@@ -1114,17 +1370,19 @@ offset & size can be used to retrieve only a part of the whole listing (defaut b
 =cut
 
 sub GetItemsForInventory {
-    my ( $minlocation, $maxlocation, $datelastseen, $branch, $offset, $size ) = @_;
+    my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
     my $dbh = C4::Context->dbh;
     my $sth;
     if ($datelastseen) {
+        $datelastseen=format_date_in_iso($datelastseen);  
         my $query =
-                "SELECT itemnumber,barcode,itemcallnumber,title,author,datelastseen
+                "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);
@@ -1132,11 +1390,12 @@ sub GetItemsForInventory {
     }
     else {
         my $query ="
-                SELECT itemnumber,barcode,itemcallnumber,title,author,datelastseen
+                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);
@@ -1145,6 +1404,7 @@ sub GetItemsForInventory {
     my @results;
     while ( my $row = $sth->fetchrow_hashref ) {
         $offset-- if ($offset);
+        $row->{datelastseen}=format_date($row->{datelastseen});
         if ( ( !$offset ) && $size ) {
             push @results, $row;
             $size--;
@@ -1170,18 +1430,19 @@ that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
 
 #'
 sub GetBiblioItemData {
-    my ($bibitem) = @_;
+    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 = ? and biblioitems.itemtype = itemtypes.itemtype"
-      );
+    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($bibitem);
-
+    $sth->execute($biblioitemnumber);
     $data = $sth->fetchrow_hashref;
-
     $sth->finish;
     return ($data);
 }    # sub &GetBiblioItemData
@@ -1201,7 +1462,7 @@ sub GetItemnumberFromBarcode {
     my $dbh = C4::Context->dbh;
 
     my $rq =
-      $dbh->prepare("SELECT itemnumber from items where items.barcode=?");
+      $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
     $rq->execute($barcode);
     my ($result) = $rq->fetchrow;
     return ($result);
@@ -1220,7 +1481,7 @@ NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebr
 sub GetBiblioItemByBiblioNumber {
     my ($biblionumber) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("Select * from biblioitems where biblionumber = ?");
+    my $sth = $dbh->prepare("Select * FROM biblioitems WHERE biblionumber = ?");
     my $count = 0;
     my @results;
 
@@ -1238,9 +1499,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
@@ -1252,16 +1513,22 @@ database.
 
 #'
 sub GetBiblioFromItemNumber {
-    my ( $itemnumber ) = @_;
+    my ( $itemnumber, $barcode ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare(
-        "SELECT * FROM biblio,items,biblioitems
-         WHERE items.itemnumber = ?
-           AND biblio.biblionumber = items.biblionumber
-           AND biblioitems.biblioitemnumber = items.biblioitemnumber"
-    );
-
-    $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);
@@ -1280,7 +1547,7 @@ sub GetBiblioFromItemNumber {
 sub GetBiblio {
     my ($biblionumber) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("Select * from biblio where biblionumber = ?");
+    my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber = ?");
     my $count = 0;
     my @results;
     $sth->execute($biblionumber);
@@ -1385,6 +1652,68 @@ sub GetItemInfosOf {
     return get_infos_of( $query, 'itemnumber' );
 }
 
+=head2 GetItemsByBiblioitemnumber
+
+=over 4
+
+GetItemsByBiblioitemnumber($biblioitemnumber);
+
+Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
+Called by moredetail.pl
+
+=back
+
+=cut
+
+sub GetItemsByBiblioitemnumber {
+    my ( $bibitem ) = @_;
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
+    # Get all items attached to a biblioitem
+    my $i = 0;
+    my @results; 
+    $sth->execute($bibitem) || die $sth->errstr;
+    while ( my $data = $sth->fetchrow_hashref ) {  
+        # 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'} );
+        if ( my $data2 = $sth2->fetchrow_hashref ) {
+            # if item is out, set the due date and who it is out too
+            $data->{'date_due'}   = $data2->{'date_due'};
+            $data->{'cardnumber'} = $data2->{'cardnumber'};
+            $data->{'borrowernumber'}   = $data2->{'borrowernumber'};
+        }
+        else {
+            # set date_due to blank, so in the template we check itemlost, and wthdrawn 
+            $data->{'date_due'} = '';                                                                                                         
+        }    # 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
+                      ORDER BY returndate desc,timestamp desc LIMIT 3";
+        $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
+        $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
+        my $i2 = 0;
+        while ( my $data2 = $sth2->fetchrow_hashref ) {
+            $data->{"timestamp$i2"} = $data2->{'timestamp'};
+            $data->{"card$i2"}      = $data2->{'cardnumber'};
+            $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
+            $i2++;
+        }
+        $sth2->finish;
+        push(@results,$data);
+    } 
+    $sth->finish;
+    return (\@results); 
+}
+
+
 =head2 GetBiblioItemInfosOf
 
 =over 4
@@ -1434,13 +1763,16 @@ sub GetMarcStructure {
     # check that framework exists
     $sth =
       $dbh->prepare(
-        "select count(*) from marc_tag_structure where frameworkcode=?");
+        "SELECT COUNT(*) FROM marc_tag_structure WHERE frameworkcode=?");
     $sth->execute($frameworkcode);
     my ($total) = $sth->fetchrow;
     $frameworkcode = "" unless ( $total > 0 );
     $sth =
       $dbh->prepare(
-"select tagfield,liblibrarian,libopac,mandatory,repeatable from marc_tag_structure where frameworkcode=? order by tagfield"
+        "SELECT tagfield,liblibrarian,libopac,mandatory,repeatable 
+        FROM marc_tag_structure 
+        WHERE frameworkcode=? 
+        ORDER BY tagfield"
       );
     $sth->execute($frameworkcode);
     my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );
@@ -1450,17 +1782,20 @@ sub GetMarcStructure {
     {
         $res->{$tag}->{lib} =
           ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
-          # why the hell do we need to explicitly decode utf8 ? 
-          # that's a good question, but we must do it...
-        $res->{$tab}->{tab}        = "";            # XXX
+        $res->{$tab}->{tab}        = "";
         $res->{$tag}->{mandatory}  = $mandatory;
         $res->{$tag}->{repeatable} = $repeatable;
     }
 
     $sth =
       $dbh->prepare(
-"select tagfield,tagsubfield,liblibrarian,libopac,tab, mandatory, repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue from marc_subfield_structure where frameworkcode=? order by tagfield,tagsubfield"
-      );
+            "SELECT tagfield,tagsubfield,liblibrarian,libopac,tab,mandatory,repeatable,authorised_value,authtypecode,value_builder,kohafield,seealso,hidden,isurl,link,defaultvalue 
+                FROM marc_subfield_structure 
+            WHERE frameworkcode=? 
+                ORDER BY tagfield,tagsubfield
+            "
+    );
+    
     $sth->execute($frameworkcode);
 
     my $subfield;
@@ -1498,12 +1833,44 @@ sub GetMarcStructure {
         $res->{$tag}->{$subfield}->{seealso}          = $seealso;
         $res->{$tag}->{$subfield}->{hidden}           = $hidden;
         $res->{$tag}->{$subfield}->{isurl}            = $isurl;
-        $res->{$tag}->{$subfield}->{link}             = $link;
+        $res->{$tag}->{$subfield}->{'link'}           = $link;
         $res->{$tag}->{$subfield}->{defaultvalue}     = $defaultvalue;
     }
     return $res;
 }
 
+=head2 GetUsedMarcStructure
+
+    the same function as GetMarcStructure expcet it just take field
+    in tab 0-9. (used field)
+    
+    my $results = GetUsedMarcStructure($frameworkcode);
+    
+    L<$results> is a ref to an array which each case containts a ref
+    to a hash which each keys is the columns from marc_subfield_structure
+    
+    L<$frameworkcode> is the framework code. 
+    
+=cut
+
+sub GetUsedMarcStructure($){
+    my $frameworkcode = shift || '';
+    my $dbh           = C4::Context->dbh;
+    my $query         = qq/
+        SELECT *
+        FROM   marc_subfield_structure
+        WHERE   tab > -1 
+            AND frameworkcode = ?
+    /;
+    my @results;
+    my $sth = $dbh->prepare($query);
+    $sth->execute($frameworkcode);
+    while (my $row = $sth->fetchrow_hashref){
+        push @results,$row;
+    }
+    return \@results;
+}
+
 =head2 GetMarcFromKohaField
 
 =over 4
@@ -1541,18 +1908,25 @@ sub GetMarcBiblio {
     my $biblionumber = shift;
     my $dbh          = C4::Context->dbh;
     my $sth          =
-      $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
+      $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
     $sth->execute($biblionumber);
-    my ($marcxml) = $sth->fetchrow;
-    MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
-    $marcxml =~ s/\x1e//g;
-    $marcxml =~ s/\x1f//g;
-    $marcxml =~ s/\x1d//g;
-    $marcxml =~ s/\x0f//g;
-    $marcxml =~ s/\x0c//g;
+     my ($marcxml) = $sth->fetchrow;
+     MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
+     $marcxml =~ s/\x1e//g;
+     $marcxml =~ s/\x1f//g;
+     $marcxml =~ s/\x1d//g;
+     $marcxml =~ s/\x0f//g;
+     $marcxml =~ s/\x0c//g;  
+#   warn $marcxml;
     my $record = MARC::Record->new();
-    $record = MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour')) if $marcxml;
-    return $record;
+    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;
+    } else {
+        return undef;
+    }
 }
 
 =head2 GetXmlBiblio
@@ -1572,7 +1946,7 @@ sub GetXmlBiblio {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
     my $sth =
-      $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
+      $dbh->prepare("SELECT marcxml FROM biblioitems WHERE biblionumber=? ");
     $sth->execute($biblionumber);
     my ($marcxml) = $sth->fetchrow;
     return $marcxml;
@@ -1583,35 +1957,41 @@ sub GetXmlBiblio {
 =over 4
 
 my $subfieldvalue =get_authorised_value_desc(
-    $tag, $subf[$i][0],$subf[$i][1], '', $taglib);
+    $tag, $subf[$i][0],$subf[$i][1], '', $taglib, $category);
 Retrieve the complete description for a given authorised value.
 
+Now takes $category and $value pair too.
+my $auth_value_desc =GetAuthorisedValueDesc(
+    '','', 'DVD' ,'','','CCODE');
+
 =back
 
 =cut
 
 sub GetAuthorisedValueDesc {
-    my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
+    my ( $tag, $subfield, $value, $framework, $tagslib, $category ) = @_;
     my $dbh = C4::Context->dbh;
-    
-    #---- branch
-    if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
-        return C4::Branch::GetBranchName($value);
-    }
 
-    #---- itemtypes
-    if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
-        return getitemtypeinfo($value);
-    }
+    if (!$category) {
+#---- branch
+        if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
+            return C4::Branch::GetBranchName($value);
+        }
+
+#---- itemtypes
+        if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
+            return getitemtypeinfo($value)->{description};
+        }
 
-    #---- "true" authorized value
-    my $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'};
+#---- "true" authorized value
+        $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
+    }
 
     if ( $category ne "" ) {
         my $sth =
-          $dbh->prepare(
-            "select lib from authorised_values where category = ? and authorised_value = ?"
-          );
+            $dbh->prepare(
+                    "SELECT lib FROM authorised_values WHERE category = ? AND authorised_value = ?"
+                    );
         $sth->execute( $category, $value );
         my $data = $sth->fetchrow_hashref;
         return $data->{'lib'};
@@ -1633,23 +2013,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;
+
 }
 
 
@@ -1721,50 +2127,45 @@ 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;
-        my $label = "su:";
-        my $flag = 0;
-        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 $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];
-            $label .= $subject_subfield->[1] . " and su-to:" unless ( $code == 9 );
-            if ( $code == 9 ) {
-                $link = "Koha-Auth-Number:".$subject_subfield->[1];
-                $flag = 1;
-            }
-            elsif ( ! $flag ) {
-                $link = $label;
-                $link =~ s/ and\ssu-to:$//;
+            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 };
             }
+            my $separator = C4::Context->preference("authoritysep") unless $counter==0;
+            # ignore $9
+            my @this_link_loop = @link_loop;
+            push @subfields_loop, {code => $code, value => $value, link_loop => \@this_link_loop, separator => $separator} unless ($subject_subfield->[0] == 9 );
+            $counter++;
         }
-        $label =~ s/su/ /g;
-        $label =~ s/://g;
-        $label =~ s/-to//g;
-        $label =~ s/ and //g;
-        push @marcsubjcts,
-          {
-            label => $label,
-            link  => $link
-          }
+                
+        push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
+        
     }
-    return \@marcsubjcts;
-}    #end GetMarcSubjects
+        return \@marcsubjects;
+}  #end getMARCsubjects
 
 =head2 GetMarcAuthors
 
@@ -1781,55 +2182,102 @@ The authors are stored in differents places depending on MARC flavour
 sub GetMarcAuthors {
     my ( $record, $marcflavour ) = @_;
     my ( $mintag, $maxtag );
+    # tagslib useful for UNIMARC author reponsabilities
+    my $tagslib = &GetMarcStructure( 1, '' ); # FIXME : we don't have the framework available, we take the default framework. May be bugguy on some setups, will be usually correct.
     if ( $marcflavour eq "MARC21" ) {
-        $mintag = "100";
-        $maxtag = "111"; 
+        $mintag = "700";
+        $maxtag = "720"; 
     }
-    else {    # assume unimarc if not marc21
-        $mintag = "701";
+    elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
+        $mintag = "700";
         $maxtag = "712";
     }
-
+    else {
+        return;
+    }
     my @marcauthors;
 
     foreach my $field ( $record->fields ) {
         next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
-        my %hash;
+        my @subfields_loop;
+        my @link_loop;
         my @subfields = $field->subfields();
         my $count_auth = 0;
-        my $and ;
+        # if there is an authority link, build the link with Koha-Auth-Number: subfield9
+        my $subfield9 = $field->subfield('9');
         for my $authors_subfield (@subfields) {
-            if (
-                $marcflavour ne 'MARC21'
-                and (
-                    ($authors_subfield->[0] eq '3') or
-                    ($authors_subfield->[0] eq '4') or
-                    ($authors_subfield->[0] eq '5')
-                )
-            )
-            {
-                next;
+            # don't load unimarc subfields 3, 5
+            next if ($marcflavour eq 'UNIMARC' and ($authors_subfield->[0] =~ (3|5) ) );
+            my $subfieldcode = $authors_subfield->[0];
+            my $value = $authors_subfield->[1];
+            my $linkvalue = $value;
+            $linkvalue =~ s/(\(|\))//g;
+            my $operator = " and " unless $count_auth==0;
+            # if we have an authority link, use that as the link, otherwise use standard searching
+            if ($subfield9) {
+                @link_loop = ({'limit' => 'Koha-Auth-Number' ,link => "$subfield9" });
             }
-            if ($count_auth ne '0'){
-                $and = " and au:";
+            else {
+                # reset $linkvalue if UNIMARC author responsibility
+                if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
+                    $linkvalue = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
+                }
+                push @link_loop, {'limit' => 'au', link => $linkvalue, operator => $operator };
             }
+            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] == 9 );
             $count_auth++;
-            my $subfieldcode = $authors_subfield->[0];
-            my $value        = $authors_subfield->[1];
-            $hash{tag}       = $field->tag;
-            $hash{value}    .= $value . " " if ($subfieldcode != 9) ;
-            $hash{link}     .= $value if ($subfieldcode eq 9);
         }
-        push @marcauthors, \%hash;
+        push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
     }
     return \@marcauthors;
 }
 
+=head2 GetMarcUrls
+
+=over 4
+
+$marcurls = GetMarcUrls($record,$marcflavour);
+Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop.
+Assumes web resources (not uncommon in MARC21 to omit resource type ind) 
+
+=back
+
+=cut
+
+sub GetMarcUrls {
+    my ($record, $marcflavour) = @_;
+    my @marcurls;
+    my $marcurl;
+    for my $field ($record->field('856')) {
+        my $url = $field->subfield('u');
+        my @notes;
+        for my $note ( $field->subfield('z')) {
+            push @notes , {note => $note};
+        }        
+        $marcurl = {  MARCURL => $url,
+                      notes => \@notes,
+                    };
+        if($marcflavour eq 'MARC21') {
+            my $s3 = $field->subfield('3');
+            my $link = $field->subfield('y');
+            $marcurl->{'linktext'} = $link || $s3 || $url ;;
+            $marcurl->{'part'} = $s3 if($link);
+            $marcurl->{'toc'} = 1 if($s3 =~ /^[Tt]able/) ;
+        } else {
+            $marcurl->{'linktext'} = $url;
+        }
+        push @marcurls, $marcurl;    
+    }
+    return \@marcurls;
+}  #end GetMarcUrls
+
 =head2 GetMarcSeries
 
 =over 4
 
-$marcseriessarray = GetMarcSeries($record,$marcflavour);
+$marcseriesarray = GetMarcSeries($record,$marcflavour);
 Get all series from the MARC record and returns them in an array.
 The series are stored in differents places depending on MARC flavour
 
@@ -1862,12 +2310,12 @@ sub GetMarcSeries {
         my $counter = 0;
         my @link_loop;
         for my $series_subfield (@subfields) {
-                       my $volume_number;
-                       undef $volume_number;
-                       # see if this is an instance of a volume
-                       if ($series_subfield->[0] eq 'v') {
-                               $volume_number=1;
-                       }
+            my $volume_number;
+            undef $volume_number;
+            # see if this is an instance of a volume
+            if ($series_subfield->[0] eq 'v') {
+                $volume_number=1;
+            }
 
             my $code = $series_subfield->[0];
             my $value = $series_subfield->[1];
@@ -1876,12 +2324,12 @@ sub GetMarcSeries {
             my $operator = " and " unless $counter==0;
             push @link_loop, {link => $linkvalue, operator => $operator };
             my $separator = C4::Context->preference("authoritysep") unless $counter==0;
-                       if ($volume_number) {
-                       push @subfields_loop, {volumenum => $value};
-                       }
-                       else {
+            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};
-                       }
+            }
             $counter++;
         }
         push @marcseries, { MARCSERIES_SUBFIELDS_LOOP => \@subfields_loop };
@@ -1898,7 +2346,7 @@ sub GetMarcSeries {
 
 =over 4
 
-$frameworkcode = GetFrameworkCode( $biblionumber )
+    $frameworkcode = GetFrameworkCode( $biblionumber )
 
 =back
 
@@ -1907,21 +2355,50 @@ $frameworkcode = GetFrameworkCode( $biblionumber )
 sub GetFrameworkCode {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth =
-      $dbh->prepare("select frameworkcode from biblio where biblionumber=?");
+    my $sth = $dbh->prepare("SELECT frameworkcode FROM biblio WHERE biblionumber=?");
     $sth->execute($biblionumber);
     my ($frameworkcode) = $sth->fetchrow;
     return $frameworkcode;
 }
 
+=head2 GetPublisherNameFromIsbn
+
+    $name = GetPublishercodeFromIsbn($isbn);
+    if(defined $name){
+        ...
+    }
+
+=cut
+
+sub GetPublisherNameFromIsbn($){
+    my $isbn = shift;
+    $isbn =~ s/[- _]//g;
+    $isbn =~ s/^0*//;
+    my @codes = (split '-', DisplayISBN($isbn));
+    my $code = $codes[0].$codes[1].$codes[2];
+    my $dbh  = C4::Context->dbh;
+    my $query = qq{
+        SELECT distinct publishercode
+        FROM   biblioitems
+        WHERE  isbn LIKE ?
+        AND    publishercode IS NOT NULL
+        LIMIT 1
+    };
+    my $sth = $dbh->prepare($query);
+    $sth->execute("$code%");
+    my $name = $sth->fetchrow;
+    return $name if length $name;
+    return undef;
+}
+
 =head2 TransformKohaToMarc
 
 =over 4
 
-$record = TransformKohaToMarc( $hash )
-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
+    $record = TransformKohaToMarc( $hash )
+    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
 
 =back
 
@@ -1933,7 +2410,7 @@ sub TransformKohaToMarc {
     my $dbh = C4::Context->dbh;
     my $sth =
     $dbh->prepare(
-        "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
+        "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
     );
     my $record = MARC::Record->new();
     foreach (keys %{$hash}) {
@@ -1947,7 +2424,7 @@ sub TransformKohaToMarc {
 
 =over 4
 
-$record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
+    $record = TransformKohaToMarcOneField( $sth, $record, $kohafieldname, $value, $frameworkcode );
 
 =back
 
@@ -1961,10 +2438,9 @@ sub TransformKohaToMarcOneField {
 
     if ( !defined $sth ) {
         my $dbh = C4::Context->dbh;
-        $sth =
-          $dbh->prepare(
-"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
-          );
+        $sth = $dbh->prepare(
+            "SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE frameworkcode=? AND kohafield=?"
+        );
     }
     $sth->execute( $frameworkcode, $kohafieldname );
     if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
@@ -1991,6 +2467,7 @@ $auth_type contains :
 - nothing : rebuild a biblio, un UNIMARC the encoding is in 100$a pos 26/27
 - UNIMARCAUTH : rebuild an authority. In UNIMARC, the encoding is in 100$a pos 13/14
 - ITEM : rebuild an item : in UNIMARC, 100$a, it's in the biblio ! (otherwise, we would get 2 100 fields !)
+
 =back
 
 =cut
@@ -2025,9 +2502,9 @@ sub TransformHtmlToXml {
         @$values[$i] =~ s/>/&gt;/g;
         @$values[$i] =~ s/"/&quot;/g;
         @$values[$i] =~ s/'/&apos;/g;
-        if ( !utf8::is_utf8( @$values[$i] ) ) {
-            utf8::decode( @$values[$i] );
-        }
+        if ( !utf8::is_utf8( @$values[$i] ) ) {
+            utf8::decode( @$values[$i] );
+        }
         if ( ( @$tags[$i] ne $prevtag ) ) {
             $j++ unless ( @$tags[$i] eq "" );
             if ( !$first ) {
@@ -2110,158 +2587,326 @@ sub TransformHtmlToXml {
         $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
         $xml .= "<subfield code=\"a\">$string</subfield>\n";
         $xml .= "</datafield>\n";
-    } 
+    }
     $xml .= MARC::File::XML::footer();
     return $xml;
 }
 
 =head2 TransformHtmlToMarc
 
-=over 4
-
-$record = TransformHtmlToMarc( $dbh, $rtags, $rsubfields, $rvalues, %indicators )
-
-=back
+    L<$record> = TransformHtmlToMarc(L<$params>,L<$cgi>)
+    L<$params> is a ref to an array as below:
+    {
+        'tag_010_indicator_531951' ,
+        'tag_010_code_a_531951_145735' ,
+        'tag_010_subfield_a_531951_145735' ,
+        'tag_200_indicator_873510' ,
+        'tag_200_code_a_873510_673465' ,
+        'tag_200_subfield_a_873510_673465' ,
+        'tag_200_code_b_873510_704318' ,
+        'tag_200_subfield_b_873510_704318' ,
+        'tag_200_code_e_873510_280822' ,
+        'tag_200_subfield_e_873510_280822' ,
+        '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 ( $dbh, $rtags, $rsubfields, $rvalues, %indicators ) = @_;
-    my $prevtag = -1;
+    my $params = shift;
+    my $cgi    = shift;
+    
+    # creating a new record
     my $record  = MARC::Record->new();
-
-    #     my %subfieldlist=();
-    my $prevvalue;    # if tag <10
-    my $field;        # if tag >=10
-    for ( my $i = 0 ; $i < @$rtags ; $i++ ) {
-        next unless @$rvalues[$i];
-
- # rebuild MARC::Record
- #             warn "0=>".@$rtags[$i].@$rsubfields[$i]." = ".@$rvalues[$i].": ";
-        if ( @$rtags[$i] ne $prevtag ) {
-            if ( $prevtag < 10 ) {
-                if ($prevvalue) {
-
-                    if ( $prevtag ne '000' ) {
-                        $record->insert_fields_ordered(
-                            ( sprintf "%03s", $prevtag ), $prevvalue );
-                    }
-                    else {
-
-                        $record->leader($prevvalue);
-
-                    }
-                }
-            }
-            else {
-                if ($field) {
-                    $record->insert_fields_ordered($field);
-                }
-            }
-            $indicators{ @$rtags[$i] } .= '  ';
-            if ( @$rtags[$i] < 10 ) {
-                $prevvalue = @$rvalues[$i];
-                undef $field;
-            }
-            else {
-                undef $prevvalue;
-                $field = MARC::Field->new(
-                    ( sprintf "%03s", @$rtags[$i] ),
-                    substr( $indicators{ @$rtags[$i] }, 0, 1 ),
-                    substr( $indicators{ @$rtags[$i] }, 1, 1 ),
-                    @$rsubfields[$i] => @$rvalues[$i]
+    my $i=0;
+    my @fields;
+    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)
+        if ($param eq 'biblionumber') {
+            my ( $biblionumbertagfield, $biblionumbertagsubfield ) =
+                &GetMarcFromKohaField( "biblio.biblionumber", '' );
+            if ($biblionumbertagfield < 10) {
+                $newfield = MARC::Field->new(
+                    $biblionumbertagfield,
+                    $cgi->param($param),
+                );
+            } else {
+                $newfield = MARC::Field->new(
+                    $biblionumbertagfield,
+                    '',
+                    '',
+                    "$biblionumbertagsubfield" => $cgi->param($param),
                 );
             }
-            $prevtag = @$rtags[$i];
-        }
-        else {
-            if ( @$rtags[$i] < 10 ) {
-                $prevvalue = @$rvalues[$i];
-            }
-            else {
-                if ( length( @$rvalues[$i] ) > 0 ) {
-                    $field->add_subfields( @$rsubfields[$i] => @$rvalues[$i] );
+            push @fields,$newfield if($newfield);
+        } 
+        elsif ($param =~ /^tag_(\d*)_indicator_/){ # new field start when having 'input name="..._indicator_..."
+            my $tag  = $1;
+            
+            my $ind1 = substr($cgi->param($param),0,1);
+            my $ind2 = substr($cgi->param($param),1,1);
+            $newfield=0;
+            my $j=$i+1;
+            
+            if($tag < 10){ # no code for theses fields
+    # in MARC editor, 000 contains the leader.
+                if ($tag eq '000' ) {
+                    $record->leader($cgi->param($params->[$j+1])) if length($cgi->param($params->[$j+1]))==24;
+    # between 001 and 009 (included)
+                } else {
+                    $newfield = MARC::Field->new(
+                        $tag,
+                        $cgi->param($params->[$j+1]),
+                    );
+                }
+    # > 009, deal with subfields
+            } else {
+                while($params->[$j] =~ /_code_/){ # browse all it's subfield
+                    my $inner_param = $params->[$j];
+                    if ($newfield){
+                        if($cgi->param($params->[$j+1])){  # 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]) ) { # 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]),
+                            );
+                        }
+                    }
+                    $j+=2;
                 }
             }
-            $prevtag = @$rtags[$i];
+            push @fields,$newfield if($newfield);
         }
+        $i++;
     }
-
-    # the last has not been included inside the loop... do it now !
-    $record->insert_fields_ordered($field) if $field;
-
-    $record->encoding('UTF-8');
-
-    #    $record->MARC::File::USMARC::update_leader();
+    
+    $record->append_fields(@fields);
     return $record;
 }
 
+# cache inverted MARC field map
+our $inverted_field_map;
+
 =head2 TransformMarcToKoha
 
 =over 4
 
-$result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
+    $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
 
 =back
 
-=cut
+Extract data from a MARC bib record into a hashref representing
+Koha biblio, biblioitems, and items fields. 
 
+=cut
 sub TransformMarcToKoha {
-    my ( $dbh, $record, $frameworkcode ) = @_;
-    my $sth =
-      $dbh->prepare(
-"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
-      );
+    my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
+
     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 );
+
+    unless (defined $inverted_field_map) {
+        $inverted_field_map = _get_inverted_marc_field_map();
     }
-    $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 );
+
+    my %tables = ();
+    if ($limit_table eq 'items') {
+        $tables{'items'} = 1;
+    } else {
+        $tables{'items'} = 1;
+        $tables{'biblio'} = 1;
+        $tables{'biblioitems'} = 1;
     }
-    $sth2 = $dbh->prepare("SHOW COLUMNS from items");
-    $sth2->execute;
-    while ( ($field) = $sth2->fetchrow ) {
-        $result =
-          &TransformMarcToKohaOneField( "items", $field, $record, $result,
-            $frameworkcode );
+
+    # traverse through record
+    MARCFIELD: foreach my $field ($record->fields()) {
+        my $tag = $field->tag();
+        next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
+        if ($field->is_control_field()) {
+            my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
+            ENTRY: foreach my $entry (@{ $kohafields }) {
+                my ($subfield, $table, $column) = @{ $entry };
+                next ENTRY unless exists $tables{$table};
+                my $key = _disambiguate($table, $column);
+                if ($result->{$key}) {
+                    unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($field->data() eq "")) {
+                        $result->{$key} .= " | " . $field->data();
+                    }
+                } else {
+                    $result->{$key} = $field->data();
+                }
+            }
+        } else {
+            # deal with subfields
+            MARCSUBFIELD: foreach my $sf ($field->subfields()) {
+                my $code = $sf->[0];
+                next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
+                my $value = $sf->[1];
+                SFENTRY: foreach my $entry (@{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} }) {
+                    my ($table, $column) = @{ $entry };
+                    next SFENTRY unless exists $tables{$table};
+                    my $key = _disambiguate($table, $column);
+                    if ($result->{$key}) {
+                        unless (($key eq "biblionumber" or $key eq "biblioitemnumber") and ($value eq "")) {
+                            $result->{$key} .= " | " . $value;
+                        }
+                    } else {
+                        $result->{$key} = $value;
+                    }
+                }
+            }
+        }
     }
 
-    #
     # 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;
+    if (exists $result->{'copyrightdate'}) {
+        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;
+    if (exists $result->{'publicationyear'}) {
+        my $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;
 }
 
+sub _get_inverted_marc_field_map {
+    my $relations = C4::Context->marcfromkohafield;
+
+    my $field_map = {};
+    my $relations = C4::Context->marcfromkohafield;
+
+    foreach my $frameworkcode (keys %{ $relations }) {
+        foreach my $kohafield (keys %{ $relations->{$frameworkcode} }) {
+            my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
+            my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
+            my ($table, $column) = split /[.]/, $kohafield, 2;
+            push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
+            push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
+        }
+    }
+    return $field_map;
+}
+
+=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;
+    }
+
+}
+
+=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
@@ -2274,9 +2919,9 @@ $result = TransformMarcToKohaOneField( $kohatable, $kohafield, $record, $result,
 
 sub TransformMarcToKohaOneField {
 
-# FIXME ? if a field has a repeatable subfield that is used in old-db, only the 1st will be retrieved...
+    # FIXME ? if a field has a repeatable subfield that is used in old-db,
+    # only the 1st will be retrieved...
     my ( $kohatable, $kohafield, $record, $result, $frameworkcode ) = @_;
-
     my $res = "";
     my ( $tagfield, $subfield ) =
       GetMarcFromKohaField( $kohatable . "." . $kohafield,
@@ -2310,6 +2955,7 @@ sub TransformMarcToKohaOneField {
     }
     return $result;
 }
+
 =head1  OTHER FUNCTIONS
 
 =head2 char_decode
@@ -2534,7 +3180,7 @@ sub PrepareItemrecordDisplay {
     my @loop_data;
     my $authorised_values_sth =
       $dbh->prepare(
-"select authorised_value,lib from authorised_values where category=? order by lib"
+"SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"
       );
     foreach my $tag ( sort keys %{$tagslib} ) {
         my $previous_tag = '';
@@ -2596,7 +3242,7 @@ sub PrepareItemrecordDisplay {
                         {
                             my $sth =
                               $dbh->prepare(
-"select branchcode,branchname from branches where branchcode = ? order by branchname"
+                                "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname"
                               );
                             $sth->execute( C4::Context->userenv->{branch} );
                             push @authorised_values, ""
@@ -2612,7 +3258,7 @@ sub PrepareItemrecordDisplay {
                         else {
                             my $sth =
                               $dbh->prepare(
-"select branchcode,branchname from branches order by branchname"
+                                "SELECT branchcode,branchname FROM branches ORDER BY branchname"
                               );
                             $sth->execute;
                             push @authorised_values, ""
@@ -2633,7 +3279,7 @@ sub PrepareItemrecordDisplay {
                     {
                         my $sth =
                           $dbh->prepare(
-"select itemtype,description from itemtypes order by description"
+                            "SELECT itemtype,description FROM itemtypes ORDER BY description"
                           );
                         $sth->execute;
                         push @authorised_values, ""
@@ -2745,6 +3391,7 @@ ModZebra( $biblionumber, $op, $server, $newRecord );
     $op is specialUpdate or delete, and is used to know what we want to do
     $server is the server that we want to update
     $newRecord is the MARC::Record containing the new record. It is usefull only when NoZebra=1, and is used to know what to add to the nozebra database. (the record in mySQL being, if it exist, the previous record, the one just before the modif. We need both : the previous and the new one.
+    
 =back
 
 =cut
@@ -2752,14 +3399,12 @@ ModZebra( $biblionumber, $op, $server, $newRecord );
 sub ModZebra {
 ###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
     my ( $biblionumber, $op, $server, $newRecord ) = @_;
-#     warn "ModZebra with : ( $biblionumber, $op, $server, $newRecord )";
     my $dbh=C4::Context->dbh;
-    #warn "SERVER:".$server;
-#
-# true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
-# at the same time
-# replaced by a zebraqueue table, that is filled with ModZebra to run.
-# the table is emptied by misc/cronjobs/zebraqueue_start.pl script
+
+    # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
+    # at the same time
+    # replaced by a zebraqueue table, that is filled with ModZebra to run.
+    # the table is emptied by misc/cronjobs/zebraqueue_start.pl script
 
     if (C4::Context->preference("NoZebra")) {
         # lock the nozebra table : we will read index lines, update them in Perl process
@@ -2775,30 +3420,31 @@ sub ModZebra {
         }
         if ($op eq 'specialUpdate') {
             # OK, we have to add or update the record
-            # 1st delete (virtually, in indexes) ...
-            %result = _DelBiblioNoZebra($biblionumber,$record,$server);
+            # 1st delete (virtually, in indexes), if record actually exists
+            if ($record) { 
+                %result = _DelBiblioNoZebra($biblionumber,$record,$server);
+            }
             # ... add the record
             %result=_AddBiblioNoZebra($biblionumber,$newRecord, $server, %result);
         } else {
             # it's a deletion, delete the record...
-#             warn "DELETE the record $biblionumber on $server".$record->as_formatted;
+            # warn "DELETE the record $biblionumber on $server".$record->as_formatted;
             %result=_DelBiblioNoZebra($biblionumber,$record,$server);
         }
         # ok, now update the database...
         my $sth = $dbh->prepare("UPDATE nozebra SET biblionumbers=? WHERE server=? AND indexname=? AND value=?");
         foreach my $key (keys %result) {
             foreach my $index (keys %{$result{$key}}) {
-#                 warn "UPDATING : $server $key , $index with :".$result{$key}->{$index};
                 $sth->execute($result{$key}->{$index}, $server, $key, $index);
             }
         }
-    $dbh->do('UNLOCK TABLES');
+        $dbh->do('UNLOCK TABLES');
 
     } else {
         #
         # we use zebra, just fill zebraqueue table
         #
-        my $sth=$dbh->prepare("insert into zebraqueue  (biblio_auth_number ,server,operation) values(?,?,?)");
+        my $sth=$dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
         $sth->execute($biblionumber,$server,$op);
         $sth->finish;
     }
@@ -2806,6 +3452,10 @@ sub ModZebra {
 
 =head2 GetNoZebraIndexes
 
+    %indexes = GetNoZebraIndexes;
+    
+    return the data from NoZebraIndexes syspref.
+
 =cut
 
 sub GetNoZebraIndexes {
@@ -2815,8 +3465,10 @@ sub GetNoZebraIndexes {
         $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;
@@ -2926,13 +3578,12 @@ sub _DelBiblioNoZebra {
     return %result;
 }
 
-=head2 _DelBiblioNoZebra($biblionumber,$record);
+=head2 _AddBiblioNoZebra($biblionumber, $record, $server, %result);
 
-    function to delete a biblio in NoZebra indexes
+    function to add a biblio in NoZebra indexes
 
 =cut
 
-
 sub _AddBiblioNoZebra {
     my ($biblionumber, $record, $server, %result)=@_;
     my $dbh = C4::Context->dbh;
@@ -2945,14 +3596,14 @@ sub _AddBiblioNoZebra {
         my ($titletag,$titlesubfield) = GetMarcFromKohaField('biblio.title');
         $title = lc($record->subfield($titletag,$titlesubfield));
     } else {
-#     warn "server : $server";
+        # warn "server : $server";
         # for authorities, the "title" is the $a mainentry
         my $authref = C4::AuthoritiesMarc::GetAuthType($record->subfield(152,'b'));
         warn "ERROR : authtype undefined for ".$record->as_formatted unless $authref;
         $title = $record->subfield($authref->{auth_tag_to_report},'a');
-        $index{'mainmainentry'}=$authref->{auth_tag_to_report}.'a';
-        $index{'mainentry'}    = $authref->{auth_tag_to_report}.'*';
-        $index{'auth_type'}    = '152b';
+        $index{'mainmainentry'} = $authref->{auth_tag_to_report}.'a';
+        $index{'mainentry'}     = $authref->{auth_tag_to_report}.'*';
+        $index{'auth_type'}     = '152b';
     }
 
     # remove blancks comma (that could cause problem when decoding the string for CQL retrieval) and regexp specific values
@@ -2982,25 +3633,25 @@ sub _AddBiblioNoZebra {
                         next unless $_; # skip  empty values (multiple spaces)
                         # if the entry is already here, improve weight
 #                         warn "managing $_";
-                        if ($result{$key}->{$_} =~ /$biblionumber,$title\-(\d);/) {
+                        if ($result{$key}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
                             my $weight=$1+1;
-                            $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
-                            $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
+                            $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                            $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
                         } else {
                             # get the value if it exist in the nozebra table, otherwise, create it
                             $sth2->execute($server,$key,$_);
                             my $existing_biblionumbers = $sth2->fetchrow;
                             # it exists
                             if ($existing_biblionumbers) {
-                                $result{$key}->{$_} =$existing_biblionumbers;
+                                $result{$key}->{"$_"} =$existing_biblionumbers;
                                 my $weight=$1+1;
-                                $result{$key}->{$_} =~ s/$biblionumber,$title\-(\d);//;
-                                $result{$key}->{$_} .= "$biblionumber,$title-$weight;";
+                                $result{$key}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                                $result{$key}->{"$_"} .= "$biblionumber,$title-$weight;";
                             # create a new ligne for this entry
                             } else {
 #                             warn "INSERT : $server / $key / $_";
                                 $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).', indexname='.$dbh->quote($key).',value='.$dbh->quote($_));
-                                $result{$key}->{$_}.="$biblionumber,$title-1;";
+                                $result{$key}->{"$_"}.="$biblionumber,$title-1;";
                             }
                         }
                     }
@@ -3014,24 +3665,24 @@ sub _AddBiblioNoZebra {
                 foreach (split / /,$line) {
                     next unless $_; # skip  empty values (multiple spaces)
                     # if the entry is already here, improve weight
-                    if ($result{'__RAW__'}->{$_} =~ /$biblionumber,$title\-(\d);/) {
+                    if ($result{'__RAW__'}->{"$_"} =~ /$biblionumber,$title\-(\d);/) {
                         my $weight=$1+1;
-                        $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
-                        $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
+                        $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                        $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
                     } else {
                         # get the value if it exist in the nozebra table, otherwise, create it
                         $sth2->execute($server,'__RAW__',$_);
                         my $existing_biblionumbers = $sth2->fetchrow;
                         # it exists
                         if ($existing_biblionumbers) {
-                            $result{'__RAW__'}->{$_} =$existing_biblionumbers;
+                            $result{'__RAW__'}->{"$_"} =$existing_biblionumbers;
                             my $weight=$1+1;
-                            $result{'__RAW__'}->{$_} =~ s/$biblionumber,$title\-(\d);//;
-                            $result{'__RAW__'}->{$_} .= "$biblionumber,$title-$weight;";
+                            $result{'__RAW__'}->{"$_"} =~ s/$biblionumber,$title\-(\d);//;
+                            $result{'__RAW__'}->{"$_"} .= "$biblionumber,$title-$weight;";
                         # create a new ligne for this entry
                         } else {
                             $dbh->do('INSERT INTO nozebra SET server='.$dbh->quote($server).',  indexname="__RAW__",value='.$dbh->quote($_));
-                            $result{'__RAW__'}->{$_}.="$biblionumber,$title-1;";
+                            $result{'__RAW__'}->{"$_"}.="$biblionumber,$title-1;";
                         }
                     }
                 }
@@ -3072,45 +3723,6 @@ sub MARCitemchange {
         }
     }
 }
-
-=head2 _koha_add_biblio
-
-=over 4
-
-_koha_add_biblio($dbh,$biblioitem);
-
-Internal function to add a biblio ($biblio is a hash with the values)
-
-=back
-
-=cut
-
-sub _koha_add_biblio {
-    my ( $dbh, $biblio, $frameworkcode ) = @_;
-    my $sth = $dbh->prepare("Select max(biblionumber) from biblio");
-    $sth->execute;
-    my $data         = $sth->fetchrow_arrayref;
-    my $biblionumber = $$data[0] + 1;
-    my $series       = 0;
-
-    if ( $biblio->{'seriestitle'} ) { $series = 1 }
-    $sth->finish;
-    $sth = $dbh->prepare(
-        "INSERT INTO biblio
-    SET biblionumber  = ?, title = ?, author = ?, copyrightdate = ?, serial = ?, seriestitle = ?, notes = ?, abstract = ?, unititle = ?, frameworkcode = ? "
-    );
-    $sth->execute(
-        $biblionumber,         $biblio->{'title'},
-        $biblio->{'author'},   $biblio->{'copyrightdate'},
-        $biblio->{'serial'},   $biblio->{'seriestitle'},
-        $biblio->{'notes'},    $biblio->{'abstract'},
-        $biblio->{'unititle'}, $frameworkcode
-    );
-
-    $sth->finish;
-    return ($biblionumber);
-}
-
 =head2 _find_value
 
 =over 4
@@ -3156,10 +3768,139 @@ 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
+
+my ($biblionumber,$error) = _koha_add_biblio($dbh,$biblioitem);
+
+Internal function to add a biblio ($biblio is a hash with the values)
+
+=back
+
+=cut
+
+sub _koha_add_biblio {
+    my ( $dbh, $biblio, $frameworkcode ) = @_;
+
+    my $error;
+
+    # set the series flag
+    my $serial = 0;
+    if ( $biblio->{'seriestitle'} ) { $serial = 1 };
+
+    my $query = 
+        "INSERT INTO biblio
+        SET frameworkcode = ?,
+            author = ?,
+            title = ?,
+            unititle =?,
+            notes = ?,
+            serial = ?,
+            seriestitle = ?,
+            copyrightdate = ?,
+            datecreated=NOW(),
+            abstract = ?
+        ";
+    my $sth = $dbh->prepare($query);
+    $sth->execute(
+        $frameworkcode,
+        $biblio->{'author'},
+        $biblio->{'title'},
+        $biblio->{'unititle'},
+        $biblio->{'notes'},
+        $serial,
+        $biblio->{'seriestitle'},
+        $biblio->{'copyrightdate'},
+        $biblio->{'abstract'}
+    );
+
+    my $biblionumber = $dbh->{'mysql_insertid'};
+    if ( $dbh->errstr ) {
+        $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
+        warn $error;
+    }
+
+    $sth->finish();
+    #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
+    return ($biblionumber,$error);
+}
+
 =head2 _koha_modify_biblio
 
 =over 4
 
+my ($biblionumber,$error) == _koha_modify_biblio($dbh,$biblio,$frameworkcode);
+
 Internal function for updating the biblio table
 
 =back
@@ -3167,91 +3908,141 @@ Internal function for updating the biblio table
 =cut
 
 sub _koha_modify_biblio {
-    my ( $dbh, $biblio ) = @_;
+    my ( $dbh, $biblio, $frameworkcode ) = @_;
+    my $error;
 
-# FIXME: this code could be made more portable by not hard-coding the values that are supposed to be in biblio table
-    my $sth =
-      $dbh->prepare(
-"Update biblio set title = ?, author = ?, abstract = ?, copyrightdate = ?, seriestitle = ?, serial = ?, unititle = ?, notes = ? where biblionumber = ?"
-      );
+    my $query = "
+        UPDATE biblio
+        SET    frameworkcode = ?,
+               author = ?,
+               title = ?,
+               unititle = ?,
+               notes = ?,
+               serial = ?,
+               seriestitle = ?,
+               copyrightdate = ?,
+               abstract = ?
+        WHERE  biblionumber = ?
+        "
+    ;
+    my $sth = $dbh->prepare($query);
+    
     $sth->execute(
-        $biblio->{'title'},       $biblio->{'author'},
-        $biblio->{'abstract'},    $biblio->{'copyrightdate'},
-        $biblio->{'seriestitle'}, $biblio->{'serial'},
-        $biblio->{'unititle'},    $biblio->{'notes'},
+        $frameworkcode,
+        $biblio->{'author'},
+        $biblio->{'title'},
+        $biblio->{'unititle'},
+        $biblio->{'notes'},
+        $biblio->{'serial'},
+        $biblio->{'seriestitle'},
+        $biblio->{'copyrightdate'},
+        $biblio->{'abstract'},
         $biblio->{'biblionumber'}
-    );
-    $sth->finish;
-    return ( $biblio->{'biblionumber'} );
+    ) if $biblio->{'biblionumber'};
+
+    if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
+        $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
+        warn $error;
+    }
+    return ( $biblio->{'biblionumber'},$error );
 }
 
-=head2 _koha_modify_biblioitem
+=head2 _koha_modify_biblioitem_nonmarc
 
 =over 4
 
-_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 $query;
-##Recalculate LC in case it changed --TG
-
-    $biblioitem->{'itemtype'}      = $dbh->quote( $biblioitem->{'itemtype'} );
-    $biblioitem->{'url'}           = $dbh->quote( $biblioitem->{'url'} );
-    $biblioitem->{'isbn'}          = $dbh->quote( $biblioitem->{'isbn'} );
-    $biblioitem->{'issn'}          = $dbh->quote( $biblioitem->{'issn'} );
-    $biblioitem->{'publishercode'} =
-      $dbh->quote( $biblioitem->{'publishercode'} );
-    $biblioitem->{'publicationyear'} =
-      $dbh->quote( $biblioitem->{'publicationyear'} );
-    $biblioitem->{'classification'} =
-      $dbh->quote( $biblioitem->{'classification'} );
-    $biblioitem->{'dewey'}        = $dbh->quote( $biblioitem->{'dewey'} );
-    $biblioitem->{'subclass'}     = $dbh->quote( $biblioitem->{'subclass'} );
-    $biblioitem->{'illus'}        = $dbh->quote( $biblioitem->{'illus'} );
-    $biblioitem->{'pages'}        = $dbh->quote( $biblioitem->{'pages'} );
-    $biblioitem->{'volumeddesc'}  = $dbh->quote( $biblioitem->{'volumeddesc'} );
-    $biblioitem->{'bnotes'}       = $dbh->quote( $biblioitem->{'bnotes'} );
-    $biblioitem->{'size'}         = $dbh->quote( $biblioitem->{'size'} );
-    $biblioitem->{'place'}        = $dbh->quote( $biblioitem->{'place'} );
-    $biblioitem->{'ccode'}        = $dbh->quote( $biblioitem->{'ccode'} );
-    $biblioitem->{'biblionumber'} =
-      $dbh->quote( $biblioitem->{'biblionumber'} );
-
-    $query = "Update biblioitems set
-        itemtype        = $biblioitem->{'itemtype'},
-        url             = $biblioitem->{'url'},
-        isbn            = $biblioitem->{'isbn'},
-        issn            = $biblioitem->{'issn'},
-        publishercode   = $biblioitem->{'publishercode'},
-        publicationyear = $biblioitem->{'publicationyear'},
-        classification  = $biblioitem->{'classification'},
-        dewey           = $biblioitem->{'dewey'},
-        subclass        = $biblioitem->{'subclass'},
-        illus           = $biblioitem->{'illus'},
-        pages           = $biblioitem->{'pages'},
-        volumeddesc     = $biblioitem->{'volumeddesc'},
-        notes           = $biblioitem->{'bnotes'},
-        size            = $biblioitem->{'size'},
-        place           = $biblioitem->{'place'},
-        ccode           = $biblioitem->{'ccode'}
-        where biblionumber = $biblioitem->{'biblionumber'}";
-
-    $dbh->do($query);
+    my $error;
+
+    # re-calculate the cn_sort, it may have changed
+    my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+
+    my $query = 
+    "UPDATE biblioitems 
+    SET biblionumber    = ?,
+        volume          = ?,
+        number          = ?,
+        itemtype        = ?,
+        isbn            = ?,
+        issn            = ?,
+        publicationyear = ?,
+        publishercode   = ?,
+        volumedate      = ?,
+        volumedesc      = ?,
+        collectiontitle = ?,
+        collectionissn  = ?,
+        collectionvolume= ?,
+        editionstatement= ?,
+        editionresponsibility = ?,
+        illus           = ?,
+        pages           = ?,
+        notes           = ?,
+        size            = ?,
+        place           = ?,
+        lccn            = ?,
+        url             = ?,
+        cn_source       = ?,
+        cn_class        = ?,
+        cn_item         = ?,
+        cn_suffix       = ?,
+        cn_sort         = ?,
+        totalissues     = ?
+        where biblioitemnumber = ?
+        ";
+    my $sth = $dbh->prepare($query);
+    $sth->execute(
+        $biblioitem->{'biblionumber'},
+        $biblioitem->{'volume'},
+        $biblioitem->{'number'},
+        $biblioitem->{'itemtype'},
+        $biblioitem->{'isbn'},
+        $biblioitem->{'issn'},
+        $biblioitem->{'publicationyear'},
+        $biblioitem->{'publishercode'},
+        $biblioitem->{'volumedate'},
+        $biblioitem->{'volumedesc'},
+        $biblioitem->{'collectiontitle'},
+        $biblioitem->{'collectionissn'},
+        $biblioitem->{'collectionvolume'},
+        $biblioitem->{'editionstatement'},
+        $biblioitem->{'editionresponsibility'},
+        $biblioitem->{'illus'},
+        $biblioitem->{'pages'},
+        $biblioitem->{'bnotes'},
+        $biblioitem->{'size'},
+        $biblioitem->{'place'},
+        $biblioitem->{'lccn'},
+        $biblioitem->{'url'},
+        $biblioitem->{'biblioitems.cn_source'},
+        $biblioitem->{'cn_class'},
+        $biblioitem->{'cn_item'},
+        $biblioitem->{'cn_suffix'},
+        $cn_sort,
+        $biblioitem->{'totalissues'},
+        $biblioitem->{'biblioitemnumber'}
+    );
     if ( $dbh->errstr ) {
-        warn "ERROR in _koha_modify_biblioitem $query";
+        $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
+        warn $error;
     }
+    return ($biblioitem->{'biblioitemnumber'},$error);
 }
 
 =head2 _koha_add_biblioitem
 
 =over 4
 
-_koha_add_biblioitem( $dbh, $biblioitem );
+my ($biblioitemnumber,$error) = _koha_add_biblioitem( $dbh, $biblioitem );
 
 Internal function to add a biblioitem
 
@@ -3261,60 +4052,87 @@ Internal function to add a biblioitem
 
 sub _koha_add_biblioitem {
     my ( $dbh, $biblioitem ) = @_;
-
-    #  my $dbh   = C4Connect;
-    my $sth = $dbh->prepare("SELECT max(biblioitemnumber) FROM biblioitems");
-    my $data;
-    my $bibitemnum;
-
-    $sth->execute;
-    $data       = $sth->fetchrow_arrayref;
-    $bibitemnum = $$data[0] + 1;
-
-    $sth->finish;
-
-    $sth = $dbh->prepare(
-        "INSERT INTO biblioitems SET
-            biblioitemnumber = ?, biblionumber    = ?,
-            volume           = ?, number          = ?,
-            classification   = ?, itemtype        = ?,
-            url              = ?, isbn            = ?,
-            issn             = ?, dewey           = ?,
-            subclass         = ?, publicationyear = ?,
-            publishercode    = ?, volumedate      = ?,
-            volumeddesc      = ?, illus           = ?,
-            pages            = ?, notes           = ?,
-            size             = ?, lccn            = ?,
-            marc             = ?, lcsort          =?,
-            place            = ?, ccode           = ?
-          "
-    );
-    my ($lcsort) =
-      calculatelc( $biblioitem->{'classification'} )
-      . $biblioitem->{'subclass'};
+    my $error;
+
+    my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+    my $query =
+    "INSERT INTO biblioitems SET
+        biblionumber    = ?,
+        volume          = ?,
+        number          = ?,
+        itemtype        = ?,
+        isbn            = ?,
+        issn            = ?,
+        publicationyear = ?,
+        publishercode   = ?,
+        volumedate      = ?,
+        volumedesc      = ?,
+        collectiontitle = ?,
+        collectionissn  = ?,
+        collectionvolume= ?,
+        editionstatement= ?,
+        editionresponsibility = ?,
+        illus           = ?,
+        pages           = ?,
+        notes           = ?,
+        size            = ?,
+        place           = ?,
+        lccn            = ?,
+        marc            = ?,
+        url             = ?,
+        cn_source       = ?,
+        cn_class        = ?,
+        cn_item         = ?,
+        cn_suffix       = ?,
+        cn_sort         = ?,
+        totalissues     = ?
+        ";
+    my $sth = $dbh->prepare($query);
     $sth->execute(
-        $bibitemnum,                     $biblioitem->{'biblionumber'},
-        $biblioitem->{'volume'},         $biblioitem->{'number'},
-        $biblioitem->{'classification'}, $biblioitem->{'itemtype'},
-        $biblioitem->{'url'},            $biblioitem->{'isbn'},
-        $biblioitem->{'issn'},           $biblioitem->{'dewey'},
-        $biblioitem->{'subclass'},       $biblioitem->{'publicationyear'},
-        $biblioitem->{'publishercode'},  $biblioitem->{'volumedate'},
-        $biblioitem->{'volumeddesc'},    $biblioitem->{'illus'},
-        $biblioitem->{'pages'},          $biblioitem->{'bnotes'},
-        $biblioitem->{'size'},           $biblioitem->{'lccn'},
-        $biblioitem->{'marc'},           $biblioitem->{'place'},
-        $lcsort,                         $biblioitem->{'ccode'}
+        $biblioitem->{'biblionumber'},
+        $biblioitem->{'volume'},
+        $biblioitem->{'number'},
+        $biblioitem->{'itemtype'},
+        $biblioitem->{'isbn'},
+        $biblioitem->{'issn'},
+        $biblioitem->{'publicationyear'},
+        $biblioitem->{'publishercode'},
+        $biblioitem->{'volumedate'},
+        $biblioitem->{'volumedesc'},
+        $biblioitem->{'collectiontitle'},
+        $biblioitem->{'collectionissn'},
+        $biblioitem->{'collectionvolume'},
+        $biblioitem->{'editionstatement'},
+        $biblioitem->{'editionresponsibility'},
+        $biblioitem->{'illus'},
+        $biblioitem->{'pages'},
+        $biblioitem->{'bnotes'},
+        $biblioitem->{'size'},
+        $biblioitem->{'place'},
+        $biblioitem->{'lccn'},
+        $biblioitem->{'marc'},
+        $biblioitem->{'url'},
+        $biblioitem->{'biblioitems.cn_source'},
+        $biblioitem->{'cn_class'},
+        $biblioitem->{'cn_item'},
+        $biblioitem->{'cn_suffix'},
+        $cn_sort,
+        $biblioitem->{'totalissues'}
     );
-    $sth->finish;
-    return ($bibitemnum);
+    my $bibitemnum = $dbh->{'mysql_insertid'};
+    if ( $dbh->errstr ) {
+        $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
+        warn $error;
+    }
+    $sth->finish();
+    return ($bibitemnum,$error);
 }
 
 =head2 _koha_new_items
 
 =over 4
 
-_koha_new_items( $dbh, $item, $barcode );
+my ($itemnumber,$error) = _koha_new_items( $dbh, $item, $barcode );
 
 =back
 
@@ -3322,96 +4140,88 @@ _koha_new_items( $dbh, $item, $barcode );
 
 sub _koha_new_items {
     my ( $dbh, $item, $barcode ) = @_;
+    my $error;
 
-    #  my $dbh   = C4Connect;
-    my $sth = $dbh->prepare("Select max(itemnumber) from items");
-    my $data;
-    my $itemnumber;
-    my $error = "";
-
-    $sth->execute;
-    $data       = $sth->fetchrow_hashref;
-    $itemnumber = $data->{'max(itemnumber)'} + 1;
-    $sth->finish;
-## Now calculate lccalnumber
-    my ($cutterextra) = itemcalculator(
-        $dbh,
-        $item->{'biblioitemnumber'},
-        $item->{'itemcallnumber'}
-    );
-
-# FIXME the "notforloan" field seems to be named "loan" in some places. workaround bugfix.
-    if ( $item->{'loan'} ) {
-        $item->{'notforloan'} = $item->{'loan'};
-    }
+    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'} ) {
-
-        $sth = $dbh->prepare(
-            "Insert into items set
-            itemnumber           = ?,     biblionumber     = ?,
-            multivolumepart      = ?,
-            biblioitemnumber     = ?,     barcode          = ?,
-            booksellerid         = ?,     dateaccessioned  = NOW(),
-            homebranch           = ?,     holdingbranch    = ?,
-            price                = ?,     replacementprice = ?,
-            replacementpricedate = NOW(), datelastseen     = NOW(),
-            multivolume          = ?,     stack            = ?,
-            itemlost             = ?,     wthdrawn         = ?,
-            paidfor              = ?,     itemnotes        = ?,
-            itemcallnumber       =?,      notforloan       = ?,
-            location             = ?,     Cutterextra      = ?
-          "
-        );
-        $sth->execute(
-            $itemnumber,                $item->{'biblionumber'},
-            $item->{'multivolumepart'}, $item->{'biblioitemnumber'},
-            $barcode,                   $item->{'booksellerid'},
-            $item->{'homebranch'},      $item->{'holdingbranch'},
-            $item->{'price'},           $item->{'replacementprice'},
-            $item->{multivolume},       $item->{stack},
-            $item->{itemlost},          $item->{wthdrawn},
-            $item->{paidfor},           $item->{'itemnotes'},
-            $item->{'itemcallnumber'},  $item->{'notforloan'},
-            $item->{'location'},        $cutterextra
-        );
-    }
-    else {
-        $sth = $dbh->prepare(
-            "INSERT INTO items SET
-            itemnumber           = ?,     biblionumber     = ?,
-            multivolumepart      = ?,
-            biblioitemnumber     = ?,     barcode          = ?,
-            booksellerid         = ?,     dateaccessioned  = ?,
-            homebranch           = ?,     holdingbranch    = ?,
-            price                = ?,     replacementprice = ?,
-            replacementpricedate = NOW(), datelastseen     = NOW(),
-            multivolume          = ?,     stack            = ?,
-            itemlost             = ?,     wthdrawn         = ?,
-            paidfor              = ?,     itemnotes        = ?,
-            itemcallnumber       = ?,     notforloan       = ?,
-            location             = ?,
-            Cutterextra          = ?
-                            "
-        );
-        $sth->execute(
-            $itemnumber,                 $item->{'biblionumber'},
-            $item->{'multivolumepart'},  $item->{'biblioitemnumber'},
-            $barcode,                    $item->{'booksellerid'},
-            $item->{'dateaccessioned'},  $item->{'homebranch'},
-            $item->{'holdingbranch'},    $item->{'price'},
-            $item->{'replacementprice'}, $item->{multivolume},
-            $item->{stack},              $item->{itemlost},
-            $item->{wthdrawn},           $item->{paidfor},
-            $item->{'itemnotes'},        $item->{'itemcallnumber'},
-            $item->{'notforloan'},       $item->{'location'},
-            $cutterextra
-        );
+        my $today = C4::Dates->new();    
+        $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
     }
+    my $query = 
+           "INSERT INTO items SET
+            biblionumber        = ?,
+            biblioitemnumber    = ?,
+            barcode             = ?,
+            dateaccessioned     = ?,
+            booksellerid        = ?,
+            homebranch          = ?,
+            price               = ?,
+            replacementprice    = ?,
+            replacementpricedate = NOW(),
+            datelastborrowed    = ?,
+            datelastseen        = NOW(),
+            stack               = ?,
+            notforloan          = ?,
+            damaged             = ?,
+            itemlost            = ?,
+            wthdrawn            = ?,
+            itemcallnumber      = ?,
+            restricted          = ?,
+            itemnotes           = ?,
+            holdingbranch       = ?,
+            paidfor             = ?,
+            location            = ?,
+            onloan              = ?,
+            issues              = ?,
+            renewals            = ?,
+            reserves            = ?,
+            cn_source           = ?,
+            cn_sort             = ?,
+            ccode               = ?,
+            itype               = ?,
+            materials           = ?,
+            uri                 = ?
+          ";
+    my $sth = $dbh->prepare($query);
+    $sth->execute(
+            $item->{'biblionumber'},
+            $item->{'biblioitemnumber'},
+            $barcode,
+            $item->{'dateaccessioned'},
+            $item->{'booksellerid'},
+            $item->{'homebranch'},
+            $item->{'price'},
+            $item->{'replacementprice'},
+            $item->{datelastborrowed},
+            $item->{stack},
+            $item->{'notforloan'},
+            $item->{'damaged'},
+            $item->{'itemlost'},
+            $item->{'wthdrawn'},
+            $item->{'itemcallnumber'},
+            $item->{'restricted'},
+            $item->{'itemnotes'},
+            $item->{'holdingbranch'},
+            $item->{'paidfor'},
+            $item->{'location'},
+            $item->{'onloan'},
+            $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 .= $sth->errstr;
+        $error.="ERROR in _koha_new_items $query".$sth->errstr;
     }
+    $sth->finish();
     return ( $itemnumber, $error );
 }
 
@@ -3419,78 +4229,48 @@ sub _koha_new_items {
 
 =over 4
 
-_koha_modify_item( $dbh, $item, $op );
+my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
 
 =back
 
 =cut
 
 sub _koha_modify_item {
-    my ( $dbh, $item, $op ) = @_;
-    $item->{'itemnum'} = $item->{'itemnumber'} unless $item->{'itemnum'};
-
-    # if all we're doing is setting statuses, just update those and get out
-    if ( $op eq "setstatus" ) {
-        my $query =
-          "UPDATE items SET itemlost=?,wthdrawn=?,binding=? WHERE itemnumber=?";
-        my @bind = (
-            $item->{'itemlost'}, $item->{'wthdrawn'},
-            $item->{'binding'},  $item->{'itemnumber'}
-        );
-        my $sth = $dbh->prepare($query);
-        $sth->execute(@bind);
-        $sth->finish;
-        return undef;
-    }
-## Now calculate lccalnumber
-    my ($cutterextra) =
-      itemcalculator( $dbh, $item->{'bibitemnum'}, $item->{'itemcallnumber'} );
-
-    my $query = "UPDATE items SET
-barcode=?,itemnotes=?,itemcallnumber=?,notforloan=?,location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,homebranch=?,cutterextra=?, onloan=?, binding=?";
-
-    my @bind = (
-        $item->{'barcode'},        $item->{'notes'},
-        $item->{'itemcallnumber'}, $item->{'notforloan'},
-        $item->{'location'},       $item->{multivolumepart},
-        $item->{multivolume},      $item->{stack},
-        $item->{wthdrawn},         $item->{holdingbranch},
-        $item->{homebranch},       $cutterextra,
-        $item->{onloan},           $item->{binding}
-    );
-    if ( $item->{'lost'} ne '' ) {
-        $query =
-"update items set biblioitemnumber=?,barcode=?,itemnotes=?,homebranch=?,
-                            itemlost=?,wthdrawn=?,itemcallnumber=?,notforloan=?,
-                             location=?,multivolumepart=?,multivolume=?,stack=?,wthdrawn=?,holdingbranch=?,cutterextra=?,onloan=?, binding=?";
-        @bind = (
-            $item->{'bibitemnum'},     $item->{'barcode'},
-            $item->{'notes'},          $item->{'homebranch'},
-            $item->{'lost'},           $item->{'wthdrawn'},
-            $item->{'itemcallnumber'}, $item->{'notforloan'},
-            $item->{'location'},       $item->{multivolumepart},
-            $item->{multivolume},      $item->{stack},
-            $item->{wthdrawn},         $item->{holdingbranch},
-            $cutterextra,              $item->{onloan},
-            $item->{binding}
-        );
-        if ( $item->{homebranch} ) {
-            $query .= ",homebranch=?";
-            push @bind, $item->{homebranch};
-        }
-        if ( $item->{holdingbranch} ) {
-            $query .= ",holdingbranch=?";
-            push @bind, $item->{holdingbranch};
-        }
+    my ( $dbh, $item ) = @_;
+    my $error;
+
+    # calculate items.cn_sort
+    if($item->{'itemcallnumber'}) {
+        # This works, even when user is setting the call number blank (in which case
+        # how would we get here to calculate new (blank) of items.cn_sort?).
+        # 
+        # Why?  Because at present the only way to update itemcallnumber is via
+        # additem.pl; since it uses a MARC data-entry form, TransformMarcToKoha
+        # already has created $item->{'items.cn_sort'} and set it to undef because the 
+        # subfield for items.cn_sort in the framework is specified as ignored, meaning
+        # that it is not supplied or passed to the form.  Thus, if the user has
+        # blanked itemcallnumber, there is already a undef value for $item->{'items.cn_sort'}.
+        #
+        # This is subtle; it is also fragile.
+        $item->{'items.cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
     }
-    $query .= " where itemnumber=?";
-    push @bind, $item->{'itemnum'};
-    if ( $item->{'replacement'} ne '' ) {
-        $query =~ s/ where/,replacementprice='$item->{'replacement'}' where/;
+    my $query = "UPDATE items SET ";
+    my @bind;
+    for my $key ( keys %$item ) {
+        $query.="$key=?,";
+        push @bind, $item->{$key};
     }
+    $query =~ s/,$//;
+    $query .= " WHERE itemnumber=?";
+    push @bind, $item->{'itemnumber'};
     my $sth = $dbh->prepare($query);
     $sth->execute(@bind);
-    $sth->finish;
+    if ( $dbh->errstr ) {
+        $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
+        warn $error;
+    }
+    $sth->finish();
+    return ($item->{'itemnumber'},$error);
 }
 
 =head2 _koha_delete_biblio
@@ -3610,131 +4390,43 @@ Internal function to delete an item record from the koha tables
 sub _koha_delete_item {
     my ( $dbh, $itemnum ) = @_;
 
-    my $sth = $dbh->prepare("select * from items where itemnumber=?");
+    # save the deleted item to deleteditems table
+    my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
     $sth->execute($itemnum);
-    my $data = $sth->fetchrow_hashref;
-    $sth->finish;
-    my $query = "Insert into deleteditems set ";
+    my $data = $sth->fetchrow_hashref();
+    $sth->finish();
+    my $query = "INSERT INTO deleteditems SET ";
     my @bind  = ();
-    foreach my $temp ( keys %$data ) {
-        $query .= "$temp = ?,";
-        push( @bind, $data->{$temp} );
+    foreach my $key ( keys %$data ) {
+        $query .= "$key = ?,";
+        push( @bind, $data->{$key} );
     }
     $query =~ s/\,$//;
-
-    #  print $query;
     $sth = $dbh->prepare($query);
     $sth->execute(@bind);
-    $sth->finish;
-    $sth = $dbh->prepare("Delete from items where itemnumber=?");
+    $sth->finish();
+
+    # delete from items table
+    $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
     $sth->execute($itemnum);
-    $sth->finish;
+    $sth->finish();
+    return undef;
 }
 
 =head1 UNEXPORTED FUNCTIONS
 
-=over 4
-
-=head2 calculatelc
-
-$lc = calculatelc($classification);
-
-=back
-
-=cut
-
-sub calculatelc {
-    my ($classification) = @_;
-    $classification =~ s/^\s+|\s+$//g;
-    my $i = 0;
-    my $lc2;
-    my $lc1;
-
-    for ( $i = 0 ; $i < length($classification) ; $i++ ) {
-        my $c = ( substr( $classification, $i, 1 ) );
-        if ( $c ge '0' && $c le '9' ) {
-
-            $lc2 = substr( $classification, $i );
-            last;
-        }
-        else {
-            $lc1 .= substr( $classification, $i, 1 );
-
-        }
-    }    #while
-
-    my $other = length($lc1);
-    if ( !$lc1 ) {
-        $other = 0;
-    }
-
-    my $extras;
-    if ( $other < 4 ) {
-        for ( 1 .. ( 4 - $other ) ) {
-            $extras .= "0";
-        }
-    }
-    $lc1 .= $extras;
-    $lc2 =~ s/^ //g;
-
-    $lc2 =~ s/ //g;
-    $extras = "";
-    ##Find the decimal part of $lc2
-    my $pos = index( $lc2, "." );
-    if ( $pos < 0 ) { $pos = length($lc2); }
-    if ( $pos >= 0 && $pos < 5 ) {
-        ##Pad lc2 with zeros to create a 5digit decimal needed in marc record to sort as numeric
-
-        for ( 1 .. ( 5 - $pos ) ) {
-            $extras .= "0";
-        }
-    }
-    $lc2 = $extras . $lc2;
-    return ( $lc1 . $lc2 );
-}
-
-=head2 itemcalculator
-
-=over 4
-
-$cutterextra = itemcalculator( $dbh, $biblioitem, $callnumber );
-
-=back
-
-=cut
-
-sub itemcalculator {
-    my ( $dbh, $biblioitem, $callnumber ) = @_;
-    my $sth =
-      $dbh->prepare(
-"select classification, subclass from biblioitems where biblioitemnumber=?"
-      );
-
-    $sth->execute($biblioitem);
-    my ( $classification, $subclass ) = $sth->fetchrow;
-    my $all         = $classification . " " . $subclass;
-    my $total       = length($all);
-    my $cutterextra = substr( $callnumber, $total - 1 );
-
-    return $cutterextra;
-}
-
 =head2 ModBiblioMarc
 
-=over 4
-
-&ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
-
-Add MARC data for a biblio to koha 
-
-Function exported, but should NOT be used, unless you really know what you're doing
-
-=back
+    &ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
+    
+    Add MARC data for a biblio to koha 
+    
+    Function exported, but should NOT be used, unless you really know what you're doing
 
 =cut
 
 sub ModBiblioMarc {
-
+    
 # pass the MARC::Record to this function, and it will create the records in the marc field
     my ( $record, $biblionumber, $frameworkcode ) = @_;
     my $dbh = C4::Context->dbh;
@@ -3748,7 +4440,7 @@ sub ModBiblioMarc {
     $sth->finish;
     my $encoding = C4::Context->preference("marcflavour");
 
-# deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
+    # deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
     if ( $encoding eq "UNIMARC" ) {
         my $string;
         if ( length($record->subfield( 100, "a" )) == 35 ) {
@@ -3767,14 +4459,12 @@ sub ModBiblioMarc {
                 MARC::Field->new( 100, "", "", "a" => $string ) );
         }
     }
-#     warn "biblionumber : ".$biblionumber;
     ModZebra($biblionumber,"specialUpdate","biblioserver",$record);
     $sth =
       $dbh->prepare(
-        "update biblioitems set marc=?,marcxml=?  where biblionumber=?");
-    $sth->execute( $record->as_usmarc(), $record->as_xml_record(),
+        "UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
+    $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding),
         $biblionumber );
-#     warn $record->as_xml_record();
     $sth->finish;
     return $biblionumber;
 }
@@ -3795,7 +4485,7 @@ Function exported, but should NOT be used, unless you really know what you're do
 
 sub AddItemInMarc {
 
-# pass the MARC::Record to this function, and it will create the records in the marc tables
+    # pass the MARC::Record to this function, and it will create the records in the marc tables
     my ( $record, $biblionumber, $frameworkcode ) = @_;
     my $newrec = &GetMarcBiblio($biblionumber);
 
@@ -3928,9 +4618,9 @@ this function return count of item with $biblionumber
 sub GetItemsCount {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
-    my $query = qq|SELECT count(*)
-                 FROM  items 
-                 WHERE biblionumber=?|;
+    my $query = "SELECT count(*)
+          FROM  items 
+          WHERE biblionumber=?";
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $count = $sth->fetchrow;  
@@ -3938,9 +4628,6 @@ sub GetItemsCount {
     return ($count);
 }
 
-
-
-
 END { }    # module clean-up code here (global destructor)
 
 1;
@@ -3956,147 +4643,3 @@ Paul POULAIN paul.poulain@free.fr
 Joshua Ferraro jmf@liblime.com
 
 =cut
-
-# $Id$
-# $Log$
-# Revision 1.214  2007/07/02 09:13:22  tipaul
-# unimarc bugfix : the encoding is in field 100 in UNIMARC. when TransformHTMLtoXML on an item, you must not automatically add a 100 field in items, otherwise there will be 2 100 fields in the biblio, which is wrong
-#
-# Revision 1.213  2007/06/25 15:01:45  tipaul
-# bugfixes on unimarc 100 handling (the field used for encoding)
-#
-# Revision 1.212  2007/06/15 13:44:44  tipaul
-# some fixes (and only fixes)
-#
-# Revision 1.211  2007/06/15 09:40:06  toins
-# do not get $3 $4 and $5 on GetMarcSubjects GetMarcAuthors on unimarc flavour.
-#
-# Revision 1.210  2007/06/13 13:03:34  toins
-# removing warn compilation.
-#
-# Revision 1.209  2007/05/23 16:19:40  tipaul
-# various bugfixes (minor) and french translation updated
-#
-# Revision 1.208  2007/05/22 09:13:54  tipaul
-# Bugfixes & improvements (various and minor) :
-# - updating templates to have tmpl_process3.pl running without any errors
-# - adding a drupal-like css for prog templates (with 3 small images)
-# - fixing some bugs in circulation & other scripts
-# - updating french translation
-# - fixing some typos in templates
-#
-# Revision 1.207  2007/05/22 08:51:19  hdl
-# Changing GetMarcStructure signature.
-# Deleting first parameter $dbh
-#
-# Revision 1.206  2007/05/21 08:44:17  btoumi
-# add security when u delete biblio :
-# u must delete linked items before delete biblio
-#
-# Revision 1.205  2007/05/11 16:04:03  btoumi
-# bug fix:
-# problem in  displayed label link  with subject in detail.tmpl
-# ex: label random => rdom
-#
-# Revision 1.204  2007/05/10 14:45:15  tipaul
-# Koha NoZebra :
-# - support for authorities
-# - some bugfixes in ordering and "CCL" parsing
-# - support for authorities <=> biblios walking
-#
-# Seems I can do what I want now, so I consider its done, except for bugfixes that will be needed i m sure !
-#
-# Revision 1.203  2007/05/03 15:16:02  tipaul
-# BUGFIX for : NoZebra
-# - NoZebra features : seems they work fine now (adding, modifying, deleting)
-# - Biblio edition major bugfix : before this commit editing a biblio resulted in an item removal in marcxml field
-#
-# Revision 1.202  2007/05/02 16:44:31  tipaul
-# NoZebra SQL index management :
-# * adding 3 subs in Biblio.pm
-# - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
-# - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
-# - _AddBiblioNoZebra, that add index entries for a biblio.
-# Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index).
-# I think the code has to be more deeply tested, but it works at least partially.
-#
-# Revision 1.201  2007/04/27 14:00:49  hdl
-# Removing $dbh from GetMarcFromKohaField (dbh is not used in this function.)
-#
-# Revision 1.200  2007/04/25 16:26:42  tipaul
-# Koha 3.0 nozebra 1st commit : the script misc/migration_tools/rebuild_nozebra.pl build the nozebra table, and, if you set NoZebra to Yes, queries will be done through zebra. TODO :
-# - add nozebra table management on biblio editing
-# - the index table content is hardcoded. I still have to add some specific systempref to let the library update it
-# - manage pagination (next/previous)
-# - manage facets
-# WHAT works :
-# - NZgetRecords : has exactly the same API & returns as zebra getQuery, except that some parameters are unused
-# - search & sort works quite good
-# - CQL parser is better that what I thought I could do : title="harry and sally" and publicationyear>2000 not itemtype=LIVR should work fine
-#
-# Revision 1.199  2007/04/24 09:07:53  tipaul
-# moving dotransfer to Biblio.pm::ModItemTransfer + some CheckReserves fixes
-#
-# Revision 1.198  2007/04/23 15:21:17  tipaul
-# renaming currenttransfers to transferstoreceive
-#
-# Revision 1.197  2007/04/18 17:00:14  tipaul
-# removing all useless %env / $env
-#
-# Revision 1.196  2007/04/17 08:48:00  tipaul
-# circulation cleaning continued: bufixing
-#
-# Revision 1.195  2007/04/04 16:46:22  tipaul
-# HUGE COMMIT : code cleaning circulation.
-#
-# some stuff to do, i'll write a mail on koha-devel NOW !
-#
-# Revision 1.194  2007/03/30 12:00:42  tipaul
-# why the hell do we need to explicitly utf8 decode this string ? I really don't know, but it seems it's mandatory, otherwise, tag descriptions are not properly encoded...
-#
-# Revision 1.193  2007/03/29 16:45:53  tipaul
-# Code cleaning of Biblio.pm (continued)
-#
-# All subs have be cleaned :
-# - removed useless
-# - merged some
-# - reordering Biblio.pm completly
-# - using only naming conventions
-#
-# Seems to have broken nothing, but it still has to be heavily tested.
-# Note that Biblio.pm is now much more efficient than previously & probably more reliable as well.
-#
-# Revision 1.192  2007/03/29 13:30:31  tipaul
-# Code cleaning :
-# == Biblio.pm cleaning (useless) ==
-# * some sub declaration dropped
-# * removed modbiblio sub
-# * removed moditem sub
-# * removed newitems. It was used only in finishrecieve. Replaced by a TransformKohaToMarc+AddItem, that is better.
-# * removed MARCkoha2marcItem
-# * removed MARCdelsubfield declaration
-# * removed MARCkoha2marcBiblio
-#
-# == Biblio.pm cleaning (naming conventions) ==
-# * MARCgettagslib renamed to GetMarcStructure
-# * MARCgetitems renamed to GetMarcItem
-# * MARCfind_frameworkcode renamed to GetFrameworkCode
-# * MARCmarc2koha renamed to TransformMarcToKoha
-# * MARChtml2marc renamed to TransformHtmlToMarc
-# * MARChtml2xml renamed to TranformeHtmlToXml
-# * zebraop renamed to ModZebra
-#
-# == MARC=OFF ==
-# * removing MARC=OFF related scripts (in cataloguing directory)
-# * removed checkitems (function related to MARC=off feature, that is completly broken in head. If someone want to reintroduce it, hard work coming...)
-# * removed getitemsbybiblioitem (used only by MARC=OFF scripts, that is removed as well)
-#
-# Revision 1.191  2007/03/29 09:42:13  tipaul
-# adding default value new feature into cataloguing. The system (definition) part has already been added by toins
-#
-# Revision 1.190  2007/03/29 08:45:19  hdl
-# Deleting ignore_errors(1) pour MARC::Charset
-#
-# Revision 1.189  2007/03/28 10:39:16  hdl
-# removing $dbh as a parameter in AuthoritiesMarc functions
-# And reporting all differences into the scripts taht relies on those functions.