bulk MARC record import - speed improved
[koha_gimpoz] / C4 / Biblio.pm
old mode 100644 (file)
new mode 100755 (executable)
index d5a01a1..f873a66
@@ -41,7 +41,7 @@ use vars qw($VERSION @ISA @EXPORT);
 # EXPORTED FUNCTIONS.
 
 # to add biblios or items
-push @EXPORT, qw( &AddBiblio &AddItem );
+push @EXPORT, qw( &AddBiblio &AddItem &AddBiblioAndItems );
 
 # to get something
 push @EXPORT, qw(
@@ -208,7 +208,7 @@ Exported function (core API) for adding a new biblio to koha.
 
 sub AddBiblio {
     my ( $record, $frameworkcode ) = @_;
-       my ($biblionumber,$biblioitemnumber,$error);
+    my ($biblionumber,$biblioitemnumber,$error);
     my $dbh = C4::Context->dbh;
     # transform the data into koha-table style data
     my $olddata = TransformMarcToKoha( $dbh, $record, $frameworkcode );
@@ -227,6 +227,115 @@ sub AddBiblio {
     return ( $biblionumber, $biblioitemnumber );
 }
 
+=head2 AddBiblioAndItems
+
+=over 4
+
+($biblionumber,$biblioitemnumber, $itemnumber_ref) = AddBiblioAndItems($record, $frameworkcode);
+
+=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.
+
+=cut
+
+sub AddBiblioAndItems {
+    my ( $record, $frameworkcode ) = @_;
+    my ($biblionumber,$biblioitemnumber,$error);
+    my @itemnumbers = ();
+    my $dbh = C4::Context->dbh;
+
+    # transform the data into koha-table style data
+    # FIXME - this paragraph copied from AddBiblio
+    my $olddata = FasterTransformMarcToKoha( $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 ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
+    foreach my $item_field ($record->field($itemtag)) {
+        # 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 = &FasterTransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
+        $item->{'biblionumber'} = $biblionumber;
+        $item->{'biblioitemnumber'} = $biblioitemnumber;
+
+        # 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'};
+        }
+
+        # 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 );
+        }
+
+        # 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 );
+        }
+
+        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));
+    }
+
+    # 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 );
+
+    # 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, \@itemnumbers);
+    
+}
+
 =head2 AddItem
 
 =over 2
@@ -257,8 +366,8 @@ sub AddItem {
     ( $item->{'biblioitemnumber'}, $itemtype ) = $sth->fetchrow;
     $sth =
       $dbh->prepare(
-        "SELECT notforloan FROM itemtypes WHERE itemtype='$itemtype'");
-    $sth->execute();
+        "SELECT notforloan FROM itemtypes WHERE itemtype=?");
+    $sth->execute( C4::Context->preference('item-level_itypes') ? $item->{'itype'} : $itemtype );
     my $notforloan = $sth->fetchrow;
     ##Change the notforloan field if $notforloan found
     if ( $notforloan > 0 ) {
@@ -283,7 +392,7 @@ sub AddItem {
 "SELECT tagfield,tagsubfield 
 FROM marc_subfield_structure
 WHERE frameworkcode=? 
-       AND kohafield=?"
+    AND kohafield=?"
       );
     &TransformKohaToMarcOneField( $sth, $record, "items.itemnumber", $itemnumber,
         $frameworkcode );
@@ -465,14 +574,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 );
         }
     }
 }
@@ -481,7 +598,7 @@ sub ModItemInMarconefield {
 
 =over
 
-&ModItemInMarc( $record, $biblionumber, $itemnumber )
+&ModItemInMarc( $record, $biblionumber, $itemnumber, $frameworkcode )
 
 =back
 
@@ -547,14 +664,14 @@ sub DelBiblio {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
     my $error;    # for error handling
-       
-       # First make sure this biblio has no items attached
-       my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
-       $sth->execute($biblionumber);
-       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 ";
-       }
+    
+    # First make sure this biblio has no items attached
+    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber=?");
+    $sth->execute($biblionumber);
+    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;
 
@@ -563,7 +680,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);
+    ModZebra($biblionumber, "recordDelete", "biblioserver", undef);
 
     # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
     $sth =
@@ -601,10 +718,10 @@ Exported function (core API) for deleting an item record in Koha.
 
 sub DelItem {
     my ( $dbh, $biblionumber, $itemnumber ) = @_;
-       
-       # check the item has no current issues
-       
-       
+    
+    # check the item has no current issues
+    
+    
     &_koha_delete_item( $dbh, $itemnumber );
 
     # get the MARC record
@@ -652,14 +769,21 @@ sub GetBiblioData {
     my ( $bibnum ) = @_;
     my $dbh = C4::Context->dbh;
 
-    my $query = "
-        SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
-        FROM biblio
+  #  my $query =  C4::Context->preference('item-level_itypes') ? 
+    #   " SELECT * , biblioitems.notes AS bnotes, biblio.notes
+    #       FROM biblio
+    #        LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
+    #       WHERE biblio.biblionumber = ?
+    #        AND biblioitems.biblionumber = biblio.biblionumber
+    #";
+    
+    my $query = " SELECT * , biblioitems.notes AS bnotes, itemtypes.notforloan as bi_notforloan, biblio.notes
+            FROM biblio
             LEFT JOIN biblioitems ON biblio.biblionumber = biblioitems.biblionumber
             LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
-        WHERE biblio.biblionumber = ?
-            AND biblioitems.biblionumber = biblio.biblionumber
-    ";
+            WHERE biblio.biblionumber = ?
+            AND biblioitems.biblionumber = biblio.biblionumber ";
+         
     my $sth = $dbh->prepare($query);
     $sth->execute($bibnum);
     my $data;
@@ -726,40 +850,43 @@ sub GetItemsInfo {
     my $query = "SELECT *,items.notforloan as itemnotforloan
                  FROM items 
                  LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-                 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
-                 LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype
-                WHERE items.biblionumber = ?
-                ORDER BY items.dateaccessioned desc
-                 ";
+                 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
+    $query .=  (C4::Context->preference('item-level_itypes')) ?
+                     " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
+                    : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
+    $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $i = 0;
     my @results;
     my ( $date_due, $count_reserves );
 
+    my $isth    = $dbh->prepare(
+        "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
+        FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
+        WHERE  itemnumber = ?
+            AND returndate IS NULL"
+       );
     while ( my $data = $sth->fetchrow_hashref ) {
         my $datedue = '';
-        my $isth    = $dbh->prepare(
-            "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname
-            FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
-            WHERE  itemnumber = ?
-                AND returndate IS NULL"
-        );
         $isth->execute( $data->{'itemnumber'} );
         if ( my $idata = $isth->fetchrow_hashref ) {
             $data->{borrowernumber} = $idata->{borrowernumber};
             $data->{cardnumber}     = $idata->{cardnumber};
             $data->{surname}     = $idata->{surname};
             $data->{firstname}     = $idata->{firstname};
-            $datedue                = format_date( $idata->{'date_due'} );
+            $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;
             }
         }
@@ -774,8 +901,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;
 
@@ -823,6 +948,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++;
     }
@@ -879,22 +1018,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 ) {
@@ -967,20 +1106,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 ) {
@@ -1135,13 +1274,14 @@ that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.
 sub GetBiblioItemData {
     my ($biblioitemnumber) = @_;
     my $dbh       = C4::Context->dbh;
-    my $sth       =
-      $dbh->prepare(
-       "SELECT *,biblioitems.notes AS bnotes
-               FROM biblioitems,biblio,itemtypes 
-       WHERE biblio.biblionumber = biblioitems.biblionumber 
-               AND biblioitemnumber = ? "
-      );
+    my $query = "SELECT *,biblioitems.notes AS bnotes
+        FROM biblio, biblioitems ";
+    unless(C4::Context->preference('item-level_itypes')) { 
+        $query .= "LEFT JOIN itemtypes on biblioitems.itemtype=itemtypes.itemtype ";
+    }    
+    $query .= " WHERE biblio.biblionumber = biblioitems.biblionumber 
+        AND biblioitemnumber = ? ";
+    my $sth       =  $dbh->prepare($query);
     my $data;
     $sth->execute($biblioitemnumber);
     $data = $sth->fetchrow_hashref;
@@ -1201,9 +1341,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
@@ -1215,16 +1355,22 @@ database.
 
 #'
 sub GetBiblioFromItemNumber {
-    my ( $itemnumber ) = @_;
+    my ( $itemnumber, $barcode ) = @_;
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare(
-        "SELECT * FROM items 
-        LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
-        LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
-         WHERE items.itemnumber = ?"
-    );
-
-    $sth->execute($itemnumber);
+    my $sth;
+    if($itemnumber) {
+        $sth=$dbh->prepare(  "SELECT * FROM items 
+            LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+            LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+             WHERE items.itemnumber = ?") ; 
+        $sth->execute($itemnumber);
+    } else {
+        $sth=$dbh->prepare(  "SELECT * FROM items 
+            LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
+            LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
+             WHERE items.barcode = ?") ; 
+        $sth->execute($barcode);
+    }
     my $data = $sth->fetchrow_hashref;
     $sth->finish;
     return ($data);
@@ -1362,31 +1508,31 @@ Called by moredetail.pl
 =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 ( $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
+        # 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'};
-               }
+            # 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         
+            # 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 = ?
@@ -1397,11 +1543,11 @@ sub GetItemsByBiblioitemnumber {
         $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++;
-               }
+            $data->{"timestamp$i2"} = $data2->{'timestamp'};
+            $data->{"card$i2"}      = $data2->{'cardnumber'};
+            $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
+            $i2++;
+        }
         $sth2->finish;
         push(@results,$data);
     } 
@@ -1465,10 +1611,10 @@ sub GetMarcStructure {
     $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 );
@@ -1485,11 +1631,11 @@ sub GetMarcStructure {
 
     $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);
@@ -1653,34 +1799,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)->{description};
+    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
+        $category = $tagslib->{$tag}->{$subfield}->{'authorised_value'}
     }
 
-    #---- "true" authorized value
-    my $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'};
@@ -1702,23 +1855,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;
+
 }
 
 
@@ -1790,48 +1969,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 = "su:";
-        my $label;
-        my $flag = 0;
-        my $authoritysep=C4::Context->preference("authoritysep");
-        for my $subject_subfield ( @subfields ) {
-            if (
-                $marcflavour ne 'MARC21'
-                and (
-                    ($subject_subfield->[0] eq '3') or
-                    ($subject_subfield->[0] eq '4') or
-                    ($subject_subfield->[0] eq '5')
-                )
-            )
-            {
-                next;
-            }
+        my $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].$authoritysep unless ( $code == 9 );
-            $link  .= " and su-to:".$subject_subfield->[1]  unless ( $code == 9 );
-            if ( $code == 9 ) {
-                $link = "an:".$subject_subfield->[1];
-                $flag = 1;
-            }
-            elsif ( ! $flag ) {
-                $link =~ s/ and\ssu-to:$//;
+            my $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/$authoritysep$//;
-        push @marcsubjcts,
-          {
-            label => $label,
-            link  => $link
-          }
+                
+        push @marcsubjects, { MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop };
+        
     }
-    return \@marcsubjcts;
-}    #end GetMarcSubjects
+        return \@marcsubjects;
+}  #end getMARCsubjects
 
 =head2 GetMarcAuthors
 
@@ -1855,35 +2031,47 @@ sub GetMarcAuthors {
         $maxtag = "720"; 
     }
     elsif ( $marcflavour eq "UNIMARC" ) {    # assume unimarc if not marc21
-        $mintag = "701";
+        $mintag = "700";
         $maxtag = "712";
     }
-       else {
-               return;
-       }
+    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;
+        # if there is an authority link, build the link with Koha-Auth-Number: subfield9
+        my $subfield9 = $field->subfield('9');
         for my $authors_subfield (@subfields) {
-                       #unimarc-specific line
-            next if ($marcflavour eq 'UNIMARC' and (($authors_subfield->[0] eq '3') or ($authors_subfield->[0] eq '5')));
+            # 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;
-            # deal with UNIMARC author responsibility
-                       if ( $marcflavour eq 'UNIMARC' and ($authors_subfield->[0] eq '4')) {
-               $value = "(".GetAuthorisedValueDesc( $field->tag(), $authors_subfield->[0], $authors_subfield->[1], '', $tagslib ).")";
-            } else {
-                $value        = $authors_subfield->[1];
+            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" });
             }
-            $hash{tag}       = $field->tag;
-            $hash{value}    .= $value . " " if ($subfieldcode != 9) ;
-            $hash{link}     .= $value if ($subfieldcode eq 9);
+            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++;
         }
-        push @marcauthors, \%hash;
+        push @marcauthors, { MARCAUTHOR_SUBFIELDS_LOOP => \@subfields_loop };
     }
     return \@marcauthors;
 }
@@ -1912,18 +2100,18 @@ sub GetMarcUrls {
         }        
         $marcurl = {  MARCURL => $url,
                       notes => \@notes,
-                                       };
-               if($marcflavour eq 'MARC21') {
-               my $s3 = $field->subfield('3');
-                       my $link = $field->subfield('y');
+                    };
+        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;
-               }
+        } else {
+            $marcurl->{'linktext'} = $url;
+        }
         push @marcurls, $marcurl;    
-       }
+    }
     return \@marcurls;
 }  #end GetMarcUrls
 
@@ -1964,12 +2152,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];
@@ -1978,12 +2166,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 };
@@ -2310,7 +2498,7 @@ sub TransformHtmlToMarc {
             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]))==25;
+                    $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(
@@ -2347,7 +2535,6 @@ sub TransformHtmlToMarc {
     }
     
     $record->append_fields(@fields);
-    warn "RESULT : ".$record->as_formatted;
     return $record;
 }
 
@@ -2355,7 +2542,7 @@ sub TransformHtmlToMarc {
 
 =over 4
 
-       $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
+    $result = TransformMarcToKoha( $dbh, $record, $frameworkcode )
 
 =back
 
@@ -2427,6 +2614,122 @@ sub TransformMarcToKoha {
 }
 
 
+# cache inverted MARC field map
+our $inverted_field_map;
+
+=head2 FasterTransformMarcToKoha
+
+=over 4
+
+    $result = FasterTransformMarcToKoha( $dbh, $record, $frameworkcode )
+
+=back
+
+Extract data from a MARC bib record into a hashref representing
+Koha biblio, biblioitems, and items fields.  This function will
+replace TransformMarcToKoha once it has been tested.
+
+=cut
+sub FasterTransformMarcToKoha {
+    my ( $dbh, $record, $frameworkcode, $limit_table ) = @_;
+
+    my $result;
+
+    unless (defined $inverted_field_map) {
+        $inverted_field_map = _get_inverted_marc_field_map();
+    }
+
+    my %tables = ();
+    if ($limit_table eq 'items') {
+        $tables{'items'} = 1;
+    } else {
+        $tables{'items'} = 1;
+        $tables{'biblio'} = 1;
+        $tables{'biblioitems'} = 1;
+    }
+
+    # 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;
+    }
+
+    # modify publicationyear to keep only the 1st year found
+    $temp = $result->{'publicationyear'};
+    $temp =~ m/c(\d\d\d\d)/;    # search cYYYY first
+    if ( $1 > 0 ) {
+        $result->{'publicationyear'} = $1;
+    }
+    else {                      # if no cYYYY, get the 1st date.
+        $temp =~ m/(\d\d\d\d)/;
+        $result->{'publicationyear'} = $1;
+    }
+    return $result;
+}
+
+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
@@ -2853,7 +3156,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, ""
@@ -2869,7 +3172,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, ""
@@ -2890,7 +3193,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, ""
@@ -3031,8 +3334,10 @@ 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 {
@@ -3072,10 +3377,13 @@ sub GetNoZebraIndexes {
     my %indexes;
     foreach my $line (split /('|"),/,$index) {
         $line =~ /(.*)=>(.*)/;
+warn $line;
         my $index = substr($1,1); # get the index, don't forget to remove initial ' or "
         my $fields = $2;
-        $index =~ s/'|"| //g;
-        $fields =~ s/'|"| //g;
+        $index =~ s/'|"|\s//g;
+
+
+        $fields =~ s/'|"|\s//g;
         $indexes{$index}=$fields;
     }
     return %indexes;
@@ -3459,46 +3767,46 @@ Internal function to add a biblio ($biblio is a hash with the values)
 sub _koha_add_biblio {
     my ( $dbh, $biblio, $frameworkcode ) = @_;
 
-       my $error;
+    my $error;
 
-       # set the series flag
+    # set the series flag
     my $serial = 0;
     if ( $biblio->{'seriestitle'} ) { $serial = 1 };
 
-       my $query = 
+    my $query = 
         "INSERT INTO biblio
-               SET frameworkcode = ?,
-                       author = ?,
-                       title = ?,
-                       unititle =?,
-                       notes = ?,
-                       serial = ?,
-                       seriestitle = ?,
-                       copyrightdate = ?,
-                       datecreated=NOW(),
-                       abstract = ?
-               ";
+        SET frameworkcode = ?,
+            author = ?,
+            title = ?,
+            unititle =?,
+            notes = ?,
+            serial = ?,
+            seriestitle = ?,
+            copyrightdate = ?,
+            datecreated=NOW(),
+            abstract = ?
+        ";
     my $sth = $dbh->prepare($query);
     $sth->execute(
-               $frameworkcode,
+        $frameworkcode,
         $biblio->{'author'},
         $biblio->{'title'},
-               $biblio->{'unititle'},
+        $biblio->{'unititle'},
         $biblio->{'notes'},
-               $serial,
+        $serial,
         $biblio->{'seriestitle'},
-               $biblio->{'copyrightdate'},
+        $biblio->{'copyrightdate'},
         $biblio->{'abstract'}
     );
 
     my $biblionumber = $dbh->{'mysql_insertid'};
-       if ( $dbh->errstr ) {
-               $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
+    if ( $dbh->errstr ) {
+        $error.="ERROR in _koha_add_biblio $query".$dbh->errstr;
         warn $error;
     }
 
     $sth->finish();
-       #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
+    #warn "LEAVING _koha_add_biblio: ".$biblionumber."\n";
     return ($biblionumber,$error);
 }
 
@@ -3516,26 +3824,26 @@ Internal function for updating the biblio table
 
 sub _koha_modify_biblio {
     my ( $dbh, $biblio, $frameworkcode ) = @_;
-       my $error;
+    my $error;
 
     my $query = "
         UPDATE biblio
         SET    frameworkcode = ?,
-                          author = ?,
-                          title = ?,
-                          unititle = ?,
-                          notes = ?,
-                          serial = ?,
-                          seriestitle = ?,
-                          copyrightdate = ?,
+               author = ?,
+               title = ?,
+               unititle = ?,
+               notes = ?,
+               serial = ?,
+               seriestitle = ?,
+               copyrightdate = ?,
                abstract = ?
         WHERE  biblionumber = ?
-               "
-       ;
+        "
+    ;
     my $sth = $dbh->prepare($query);
     
     $sth->execute(
-               $frameworkcode,
+        $frameworkcode,
         $biblio->{'author'},
         $biblio->{'title'},
         $biblio->{'unititle'},
@@ -3543,12 +3851,12 @@ sub _koha_modify_biblio {
         $biblio->{'serial'},
         $biblio->{'seriestitle'},
         $biblio->{'copyrightdate'},
-               $biblio->{'abstract'},
+        $biblio->{'abstract'},
         $biblio->{'biblionumber'}
     ) if $biblio->{'biblionumber'};
 
     if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
-               $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
+        $error.="ERROR in _koha_modify_biblio $query".$dbh->errstr;
         warn $error;
     }
     return ( $biblio->{'biblionumber'},$error );
@@ -3569,80 +3877,80 @@ via ModBiblioMarc
 
 sub _koha_modify_biblioitem_nonmarc {
     my ( $dbh, $biblioitem ) = @_;
-       my $error;
+    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'} );
+    # 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                  = ?,
+    my $query = 
+    "UPDATE biblioitems 
+    SET biblionumber    = ?,
+        volume          = ?,
+        number          = ?,
         itemtype        = ?,
         isbn            = ?,
         issn            = ?,
-               publicationyear = ?,
+        publicationyear = ?,
         publishercode   = ?,
-               volumedate      = ?,
-               volumedesc      = ?,
-               collectiontitle = ?,
-               collectionissn  = ?,
-               collectionvolume= ?,
-               editionstatement= ?,
-               editionresponsibility = ?,
-               illus                   = ?,
-               pages                   = ?,
-               notes                   = ?,
-               size                    = ?,
-               place                   = ?,
-               lccn                    = ?,
-               url                     = ?,
-        cn_source              = ?,
+        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     = ?
+        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'}
-       );
+        ";
+    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 ) {
-               $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
+        $error.="ERROR in _koha_modify_biblioitem_nonmarc $query".$dbh->errstr;
         warn $error;
     }
-       return ($biblioitem->{'biblioitemnumber'},$error);
+    return ($biblioitem->{'biblioitemnumber'},$error);
 }
 
 =head2 _koha_add_biblioitem
@@ -3659,9 +3967,9 @@ Internal function to add a biblioitem
 
 sub _koha_add_biblioitem {
     my ( $dbh, $biblioitem ) = @_;
-       my $error;
+    my $error;
 
-       my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
+    my ($cn_sort) = GetClassSort($biblioitem->{'biblioitems.cn_source'}, $biblioitem->{'cn_class'}, $biblioitem->{'cn_item'} );
     my $query =
     "INSERT INTO biblioitems SET
         biblionumber    = ?,
@@ -3694,7 +4002,7 @@ sub _koha_add_biblioitem {
         cn_sort         = ?,
         totalissues     = ?
         ";
-       my $sth = $dbh->prepare($query);
+    my $sth = $dbh->prepare($query);
     $sth->execute(
         $biblioitem->{'biblionumber'},
         $biblioitem->{'volume'},
@@ -3728,8 +4036,8 @@ sub _koha_add_biblioitem {
     );
     my $bibitemnum = $dbh->{'mysql_insertid'};
     if ( $dbh->errstr ) {
-               $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
-               warn $error;
+        $error.="ERROR in _koha_add_biblioitem $query".$dbh->errstr;
+        warn $error;
     }
     $sth->finish();
     return ($bibitemnum,$error);
@@ -3747,80 +4055,88 @@ my ($itemnumber,$error) = _koha_new_items( $dbh, $item, $barcode );
 
 sub _koha_new_items {
     my ( $dbh, $item, $barcode ) = @_;
-       my $error;
+    my $error;
 
     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'} ) {
-               my $today = C4::Dates->new();    
-               $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
-       }
-       my $query = 
+        my $today = C4::Dates->new();    
+        $item->{'dateaccessioned'} =  $today->output("iso"); #TODO: check time issues
+    }
+    my $query = 
            "INSERT INTO items SET
-                       biblionumber            = ?,
+            biblionumber        = ?,
             biblioitemnumber    = ?,
-                       barcode                 = ?,
-                       dateaccessioned         = ?,
-                       booksellerid        = ?,
+            barcode             = ?,
+            dateaccessioned     = ?,
+            booksellerid        = ?,
             homebranch          = ?,
             price               = ?,
-                       replacementprice        = ?,
+            replacementprice    = ?,
             replacementpricedate = NOW(),
-                       datelastborrowed        = ?,
-                       datelastseen            = NOW(),
-                       stack                   = ?,
-                       notforloan                      = ?,
-                       damaged                         = ?,
-            itemlost           = ?,
-                       wthdrawn                = ?,
-                       itemcallnumber          = ?,
-                       restricted                      = ?,
-                       itemnotes                       = ?,
-                       holdingbranch           = ?,
-            paidfor            = ?,
-                       location                        = ?,
-                       onloan                          = ?,
-                       cn_source                       = ?,
-                       cn_sort                         = ?,
-                       ccode                           = ?,
-                       materials                       = ?,
-                       uri                             = ?
+            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'},
+    $sth->execute(
+            $item->{'biblionumber'},
+            $item->{'biblioitemnumber'},
             $barcode,
-                       $item->{'dateaccessioned'},
-                       $item->{'booksellerid'},
+            $item->{'dateaccessioned'},
+            $item->{'booksellerid'},
             $item->{'homebranch'},
             $item->{'price'},
-                       $item->{'replacementprice'},
-                       $item->{datelastborrowed},
-                       $item->{stack},
-                       $item->{'notforloan'},
-                       $item->{'damaged'},
+            $item->{'replacementprice'},
+            $item->{datelastborrowed},
+            $item->{stack},
+            $item->{'notforloan'},
+            $item->{'damaged'},
             $item->{'itemlost'},
-                       $item->{'wthdrawn'},
-                       $item->{'itemcallnumber'},
+            $item->{'wthdrawn'},
+            $item->{'itemcallnumber'},
             $item->{'restricted'},
-                       $item->{'itemnotes'},
-                       $item->{'holdingbranch'},
-                       $item->{'paidfor'},
-                       $item->{'location'},
-                       $item->{'onloan'},
-                       $item->{'items.cn_source'},
-                       $items_cn_sort,
-                       $item->{'ccode'},
-                       $item->{'materials'},
-                       $item->{'uri'},
+            $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.="ERROR in _koha_new_items $query".$sth->errstr;
     }
-       $sth->finish();
+    $sth->finish();
     return ( $itemnumber, $error );
 }
 
@@ -3836,18 +4152,30 @@ my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
 
 sub _koha_modify_item {
     my ( $dbh, $item ) = @_;
-       my $error;
-
-       # calculate items.cn_sort
-    $item->{'cn_sort'} = GetClassSort($item->{'items.cn_source'}, $item->{'itemcallnumber'}, "");
-
+    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'}, "");
+    }
     my $query = "UPDATE items SET ";
-       my @bind;
-       for my $key ( keys %$item ) {
-               $query.="$key=?,";
-               push @bind, $item->{$key};
+    my @bind;
+    for my $key ( keys %$item ) {
+        $query.="$key=?,";
+        push @bind, $item->{$key};
     }
-       $query =~ s/,$//;
+    $query =~ s/,$//;
     $query .= " WHERE itemnumber=?";
     push @bind, $item->{'itemnumber'};
     my $sth = $dbh->prepare($query);
@@ -3857,7 +4185,7 @@ sub _koha_modify_item {
         warn $error;
     }
     $sth->finish();
-       return ($item->{'itemnumber'},$error);
+    return ($item->{'itemnumber'},$error);
 }
 
 =head2 _koha_delete_biblio
@@ -3977,7 +4305,7 @@ Internal function to delete an item record from the koha tables
 sub _koha_delete_item {
     my ( $dbh, $itemnum ) = @_;
 
-       # save the deleted item to deleteditems table
+    # save the deleted item to deleteditems table
     my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
     $sth->execute($itemnum);
     my $data = $sth->fetchrow_hashref();
@@ -3993,11 +4321,11 @@ sub _koha_delete_item {
     $sth->execute(@bind);
     $sth->finish();
 
-       # delete from items table
+    # delete from items table
     $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
     $sth->execute($itemnum);
     $sth->finish();
-       return undef;
+    return undef;
 }
 
 =head1 UNEXPORTED FUNCTIONS
@@ -4206,8 +4534,8 @@ sub GetItemsCount {
     my ( $biblionumber ) = @_;
     my $dbh = C4::Context->dbh;
     my $query = "SELECT count(*)
-                 FROM  items 
-                 WHERE biblionumber=?";
+          FROM  items 
+          WHERE biblionumber=?";
     my $sth = $dbh->prepare($query);
     $sth->execute($biblionumber);
     my $count = $sth->fetchrow;