package C4::Biblio;

# Copyright 2000-2002 Katipo Communications
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA  02111-1307 USA

use strict;

require Exporter;
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::Log; # logaction

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 ); };

@ISA = qw( Exporter );

# EXPORTED FUNCTIONS.

# to add biblios or items
push @EXPORT, qw( &AddBiblio &AddItem );

# to get something
push @EXPORT, qw(
  &GetBiblio
  &GetBiblioData
  &GetBiblioItemData
  &GetBiblioItemInfosOf
  &GetBiblioItemByBiblioNumber
  &GetBiblioFromItemNumber
  
  &GetItemInfosOf
  &GetItemStatus
  &GetItemLocation

  &GetItemsInfo
  &GetItemFromBarcode
  &getitemsbybiblioitem
  &get_itemnumbers_of
  &GetAuthorisedValueDesc
  &GetXmlBiblio
);

# To modify something
push @EXPORT, qw(
  &ModBiblio
  &ModItem
  &ModBiblioframework
);

# To delete something
push @EXPORT, qw(
  &DelBiblio
  &DelItem
);

# Marc related functions
push @EXPORT, qw(
  &MARCfind_marc_from_kohafield
  &MARCfind_frameworkcode
  &MARCgettagslib
  &MARCmoditemonefield
  &MARCaddbiblio
  &MARCadditem
  &MARCmodbiblio
  &MARCmoditem
  &MARCkoha2marcBiblio
  &MARCmarc2koha
  &MARCkoha2marcItem
  &MARChtml2marc
  &MARChtml2xml
  &MARCgetitem
  &MARCaddword
  &MARCdelword
  &MARCdelsubfield
  &GetMarcNotes
  &GetMarcSubjects
  &GetMarcBiblio
  &GetMarcAuthors
  &GetMarcSeries
  &Koha2Marc
);

# Others functions
push @EXPORT, qw(
  &PrepareItemrecordDisplay
  &zebraop
  &char_decode
  &itemcalculator
  &calculatelc
);

# OLD functions,
push @EXPORT, qw(
  &newitems
  &modbiblio
  &modbibitem
  &moditem
  &checkitems
);

use MARC::Charset;
MARC::Charset->ignore_errors(1);
=head1 NAME

C4::Biblio - acquisitions and cataloging management functions

=head1 DESCRIPTION

Biblio.pm contains functions for managing storage and editing of bibliographic data within Koha. Most of the functions in this module are used for cataloging records: adding, editing, or removing biblios, biblioitems, or items. Koha's stores bibliographic information in three places:

=over 4

=item 1. in the biblio,biblioitems,items, etc tables, which are limited to a one-to-one mapping to underlying MARC data

=item 2. as raw MARC in the Zebra index and storage engine

=item 3. as raw MARC the biblioitems.marc

=back

In the 2.4 version of Koha, the authoritative record-level information is in biblioitems.marc and the authoritative items information is in the items table.

Because the data isn't completely normalized there's a chance for information to get out of sync. The design choice to go with a un-normalized schema was driven by performance and stability concerns:

=over 4

=item 1. Compared with MySQL, Zebra is slow to update an index for small data changes -- especially for proc-intensive operations like circulation

=item 2. Zebra's index has been known to crash and a backup of the data is necessary to rebuild it in such cases

=back

Because of this design choice, the process of managing storage and editing is a bit convoluted. Historically, Biblio.pm's grown to an unmanagable size and as a result we have several types of functions currently:

=over 4

=item 1. Add*/Mod*/Del*/ - high-level external functions suitable for being called from external scripts to manage the collection

=item 2. _koha_* - low-level internal functions for managing the koha tables

=item 3. MARC* functions for interacting with the MARC data in both biblioitems.marc Zebra (biblioitems.marc is authoritative)

=item 4. Zebra functions used to update the Zebra index

=item 5. internal helper functions such as char_decode, checkitems, etc. Some of these probably belong in Koha.pm

=item 6. other functions that don't belong in Biblio.pm that will be cleaned out in time. (like MARCfind_marc_from_kohafield which belongs in Search.pm)

In time, as we solidify the new API these older functions will be weeded out.

=back

=head1 EXPORTED FUNCTIONS

=head2 AddBiblio

($biblionumber,$biblioitemnumber) = AddBiblio($record,$frameworkcode);

Exported function (core API) for adding a new biblio to koha.

=cut

sub AddBiblio {
    my ( $record, $frameworkcode ) = @_;
    my $oldbibnum;
    my $oldbibitemnum;
    my $dbh = C4::Context->dbh;
    # transform the data into koha-table style data
    my $olddata = MARCmarc2koha( $dbh, $record, $frameworkcode );
    $oldbibnum = _koha_add_biblio( $dbh, $olddata, $frameworkcode );
    $olddata->{'biblionumber'} = $oldbibnum;
    $oldbibitemnum = _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.
    # NOTE : Works only if the field is ONLY for biblionumber and biblioitemnumber
    # (steve and paul : thinks 090 is a good choice)
    my $sth =
      $dbh->prepare(
        "SELECT tagfield,tagsubfield
         FROM marc_subfield_structure
         WHERE kohafield=?"
      );
    $sth->execute("biblio.biblionumber");
    ( my $tagfield1, my $tagsubfield1 ) = $sth->fetchrow;
    $sth->execute("biblioitems.biblioitemnumber");
    ( my $tagfield2, my $tagsubfield2 ) = $sth->fetchrow;

    my $newfield;

    # biblionumber & biblioitemnumber are in different fields
    if ( $tagfield1 != $tagfield2 ) {

        # deal with biblionumber
        if ( $tagfield1 < 10 ) {
            $newfield = MARC::Field->new( $tagfield1, $oldbibnum, );
        }
        else {
            $newfield =
              MARC::Field->new( $tagfield1, '', '',
                "$tagsubfield1" => $oldbibnum, );
        }

        # drop old field and create new one...
        my $old_field = $record->field($tagfield1);
        $record->delete_field($old_field);
        $record->append_fields($newfield);

        # deal with biblioitemnumber
        if ( $tagfield2 < 10 ) {
            $newfield = MARC::Field->new( $tagfield2, $oldbibitemnum, );
        }
        else {
            $newfield =
              MARC::Field->new( $tagfield2, '', '',
                "$tagsubfield2" => $oldbibitemnum, );
        }
        # drop old field and create new one...
        $old_field = $record->field($tagfield2);
        $record->delete_field($old_field);
        $record->insert_fields_ordered($newfield);

# biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
    }
    else {
        my $newfield = MARC::Field->new(
            $tagfield1, '', '',
            "$tagsubfield1" => $oldbibnum,
            "$tagsubfield2" => $oldbibitemnum
        );

        # drop old field and create new one...
        my $old_field = $record->field($tagfield1);
        $record->delete_field($old_field);
        $record->insert_fields_ordered($newfield);
    }

    ###NEU specific add cataloguers cardnumber as well
    my $cardtag = C4::Context->preference('cataloguersfield');
    if ($cardtag) {
        my $tag  = substr( $cardtag, 0, 3 );
        my $subf = substr( $cardtag, 3, 1 );
        my $me        = C4::Context->userenv;
        my $cataloger = $me->{'cardnumber'} if ($me);
        my $newtag    = MARC::Field->new( $tag, '', '', $subf => $cataloger )
          if ($me);
        $record->delete_field($newtag);
        $record->insert_fields_ordered($newtag);
    }

    # now add the record
    my $biblionumber =
      MARCaddbiblio( $record, $oldbibnum, $frameworkcode );
      
    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$biblionumber,"biblio") 
        if C4::Context->preference("CataloguingLog");
      
    return ( $biblionumber, $oldbibitemnum );
}

=head2 AddItem

$biblionumber = AddItem( $record, $biblionumber)

Exported function (core API) for adding a new item to Koha

=cut

sub AddItem {
    my ( $record, $biblionumber ) = @_;
    my $dbh = C4::Context->dbh;
    
    # add item in old-DB
    my $frameworkcode = MARCfind_frameworkcode( $biblionumber );
    my $item = &MARCmarc2koha( $dbh, $record, $frameworkcode );

    # needs old biblionumber and biblioitemnumber
    $item->{'biblionumber'} = $biblionumber;
    my $sth =
      $dbh->prepare(
        "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();
    my $notforloan = $sth->fetchrow;
    ##Change the notforloan field if $notforloan found
    if ( $notforloan > 0 ) {
        $item->{'notforloan'} = $notforloan;
        &MARCitemchange( $record, "items.notforloan", $notforloan );
    }
    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( $record, "items.dateaccessioned", $date );
    }
    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=?"
      );
    &MARCkoha2marcOnefield( $sth, $record, "items.itemnumber", $itemnumber,
        $frameworkcode );

    ##NEU specific add cataloguers cardnumber as well
    my $cardtag = C4::Context->preference('itemcataloguersubfield');
    if ($cardtag) {
        $sth->execute( $frameworkcode, "items.itemnumber" );
        my ( $itemtag, $subtag ) = $sth->fetchrow;
        my $me         = C4::Context->userenv;
        my $cataloguer = $me->{'cardnumber'} if ($me);
        my $newtag     = $record->field($itemtag);
        $newtag->update( $cardtag => $cataloguer ) if ($me);
        $record->delete_field($newtag);
        $record->append_fields($newtag);
    }

    # add the item
    &MARCadditem( $record, $item->{'biblionumber'},$frameworkcode );
    
    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item") 
        if C4::Context->preference("CataloguingLog");
    
    return ($item->{biblionumber}, $item->{biblioitemnumber},$itemnumber);
}

=head2 ModBiblio

ModBiblio( $record,$biblionumber,$frameworkcode);

Exported function (core API) to modify a biblio

=cut

sub ModBiblio {
    my ( $record, $biblionumber, $frameworkcode ) = @_;
    
    if (C4::Context->preference("CataloguingLog")) {    
        my $newrecord = GetMarcBiblio($biblionumber);
        &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$biblionumber,$newrecord->as_formatted) 
    }
    
    my $dbh = C4::Context->dbh;
    
    $frameworkcode = "" unless $frameworkcode;

    # update the MARC record with the new record data
    &MARCmodbiblio( $dbh, $biblionumber, $record, $frameworkcode, 1 );

    # load the koha-table data object
    my $oldbiblio = MARCmarc2koha( $dbh, $record, $frameworkcode );

    # modify the other koha tables
    my $oldbiblionumber = _koha_modify_biblio( $dbh, $oldbiblio );
    _koha_modify_biblioitem( $dbh, $oldbiblio );

    return 1;
}

=head2 ModItem

Exported function (core API) for modifying an item in Koha.

=cut

sub ModItem {
    my ( $record, $biblionumber, $itemnumber, $delete, $new_item_hashref )
      = @_;
      
    #logging
    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$record->as_formatted) 
        if C4::Context->preference("CataloguingLog");
      
    my $dbh = C4::Context->dbh;
    
    # if we have a MARC record, we're coming from cataloging and so
    # we do the whole routine: update the MARC and zebra, then update the koha
    # tables
    if ($record) {
        my $frameworkcode = MARCfind_frameworkcode( $biblionumber );
        MARCmoditem( $record, $biblionumber, $itemnumber, $frameworkcode, $delete );
        my $olditem       = MARCmarc2koha( $dbh, $record, $frameworkcode );
        _koha_modify_item( $dbh, $olditem );
        return $biblionumber;
    }

    # otherwise, we're just looking to modify something quickly
    # (like a status) so we just update the koha tables
    elsif ($new_item_hashref) {
        _koha_modify_item( $dbh, $new_item_hashref );
    }
}

=head2 ModBiblioframework

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");
        
        warn "IN ModBiblioframework";
    $sth->execute($frameworkcode);
    return 1;
}

=head2 DelBiblio

my $error = &DelBiblio($dbh,$biblionumber);

Exported function (core API) for deleting a biblio in koha.

Deletes biblio record from Zebra and Koha tables (biblio,biblioitems,items)

Also backs it up to deleted* tables

Checks to make sure there are not issues on any of the items

return:
C<$error> : undef unless an error occurs

=cut

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 biblioitemnumber FROM biblioitems WHERE biblionumber=?");
    $sth->execute($biblionumber);
    while ( my $biblioitemnumber = $sth->fetchrow ) {
        my @issues = C4::Circulation::Circ2::itemissues($biblioitemnumber);
        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";
            }
        }
    }
    return $error if $error;

    # Delete in Zebra
    zebraop($biblionumber,"delete_record","biblioserver");

    # delete biblio from Koha tables and save in deletedbiblio
    $error = &_koha_delete_biblio( $dbh, $biblionumber );

    # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
    $sth =
      $dbh->prepare(
        "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
    $sth->execute($biblionumber);
    while ( my $biblioitemnumber = $sth->fetchrow ) {

        # delete this biblioitem
        $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_items( $dbh, $itemnumber );
            return $error if $error;
        }
    }
    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$biblionumber,"") 
        if C4::Context->preference("CataloguingLog");
    return;
}

=head2 DelItem

DelItem( $biblionumber, $itemnumber );

Exported function (core API) for deleting an item record in Koha.

=cut

sub DelItem {
    my ( $biblionumber, $itemnumber ) = @_;
    my $dbh = C4::Context->dbh;
    &_koha_delete_item( $dbh, $itemnumber );
    my $newrec = &MARCdelitem( $biblionumber, $itemnumber );
    &MARCaddbiblio( $newrec, $biblionumber, MARCfind_frameworkcode($biblionumber) );
    &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item") 
        if C4::Context->preference("CataloguingLog");
}

=head2 GetBiblioData

  $data = &GetBiblioData($biblionumber, $type);

Returns information about the book with the given biblionumber.

C<$type> is ignored.

C<&GetBiblioData> returns a reference-to-hash. The keys are the fields in
the C<biblio> and C<biblioitems> tables in the
Koha database.

In addition, C<$data-E<gt>{subject}> is the list of the book's
subjects, separated by C<" , "> (space, comma, space).

If there are multiple biblioitems with the given biblionumber, only
the first one is considered.

=cut

#'
sub GetBiblioData {
    my ( $bibnum, $type ) = @_;
    my $dbh = C4::Context->dbh;

    my $query = "
        SELECT * , biblioitems.notes AS bnotes, 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
    ";
    my $sth = $dbh->prepare($query);
    $sth->execute($bibnum);
    my $data;
    $data = $sth->fetchrow_hashref;
    $sth->finish;

    return ($data);
}    # sub GetBiblioData


=head2 GetItemsInfo

  @results = &GetItemsInfo($biblionumber, $type);

Returns information about books with the given biblionumber.

C<$type> may be either C<intra> or anything else. If it is not set to
C<intra>, then the search will exclude lost, very overdue, and
withdrawn items.

C<&GetItemsInfo> returns a list of references-to-hash. Each element
contains a number of keys. Most of them are table items from the
C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
Koha database. Other keys include:

=over 4

=item C<$data-E<gt>{branchname}>

The name (not the code) of the branch to which the book belongs.

=item C<$data-E<gt>{datelastseen}>

This is simply C<items.datelastseen>, except that while the date is
stored in YYYY-MM-DD format in the database, here it is converted to
DD/MM/YYYY format. A NULL date is returned as C<//>.

=item C<$data-E<gt>{datedue}>

=item C<$data-E<gt>{class}>

This is the concatenation of C<biblioitems.classification>, the book's
Dewey code, and C<biblioitems.subclass>.

=item C<$data-E<gt>{ocount}>

I think this is the number of copies of the book available.

=item C<$data-E<gt>{order}>

If this is set, it is set to C<One Order>.

=back

=cut

#'
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
                 ";
    my $sth = $dbh->prepare($query);
    $sth->execute($biblionumber);
    my $i = 0;
    my @results;
    my ( $date_due, $count_reserves );

    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'} );
        }
        if ( $datedue eq '' ) {
            #$datedue="Available";
            my ( $restype, $reserves ) =
              C4::Reserves2::CheckReserves( $data->{'itemnumber'} );
            if ($restype) {

                #$datedue=$restype;
                $count_reserves = $restype;
            }
        }
        $isth->finish;

        #get branch information.....
        my $bsth = $dbh->prepare(
            "SELECT * FROM branches WHERE branchcode = ?
        "
        );
        $bsth->execute( $data->{'holdingbranch'} );
        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;

        # get notforloan complete status if applicable
        my $sthnflstatus = $dbh->prepare(
            'SELECT authorised_value
            FROM   marc_subfield_structure
            WHERE  kohafield="items.notforloan"
        '
        );

        $sthnflstatus->execute;
        my ($authorised_valuecode) = $sthnflstatus->fetchrow;
        if ($authorised_valuecode) {
            $sthnflstatus = $dbh->prepare(
                "SELECT lib FROM authorised_values
                 WHERE  category=?
                 AND authorised_value=?"
            );
            $sthnflstatus->execute( $authorised_valuecode,
                $data->{itemnotforloan} );
            my ($lib) = $sthnflstatus->fetchrow;
            $data->{notforloan} = $lib;
        }

        # my stack procedures
        my $stackstatus = $dbh->prepare(
            'SELECT authorised_value
             FROM   marc_subfield_structure
             WHERE  kohafield="items.stack"
        '
        );
        $stackstatus->execute;

        ($authorised_valuecode) = $stackstatus->fetchrow;
        if ($authorised_valuecode) {
            $stackstatus = $dbh->prepare(
                "SELECT lib
                 FROM   authorised_values
                 WHERE  category=?
                 AND    authorised_value=?
            "
            );
            $stackstatus->execute( $authorised_valuecode, $data->{stack} );
            my ($lib) = $stackstatus->fetchrow;
            $data->{stack} = $lib;
        }
        $results[$i] = $data;
        $i++;
    }
    $sth->finish;

    return (@results);
}

=head2 getitemstatus

  $itemstatushash = &getitemstatus($fwkcode);
  returns information about status.
  Can be MARC dependant.
  fwkcode is optional.
  But basically could be can be loan or not
  Create a status selector with the following code

=head3 in PERL SCRIPT

my $itemstatushash = getitemstatus;
my @itemstatusloop;
foreach my $thisstatus (keys %$itemstatushash) {
    my %row =(value => $thisstatus,
                statusname => $itemstatushash->{$thisstatus}->{'statusname'},
            );
    push @itemstatusloop, \%row;
}
$template->param(statusloop=>\@itemstatusloop);


=head3 in TEMPLATE  
            <select name="statusloop">
                <option value="">Default</option>
            <!-- TMPL_LOOP name="statusloop" -->
                <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
            <!-- /TMPL_LOOP -->
            </select>

=cut

sub GetItemStatus {

    # returns a reference to a hash of references to status...
    my ($fwk) = @_;
    my %itemstatus;
    my $dbh = C4::Context->dbh;
    my $sth;
    $fwk = '' unless ($fwk);
    my ( $tag, $subfield ) =
      MARCfind_marc_from_kohafield( $dbh, "items.notforloan", $fwk );
    if ( $tag and $subfield ) {
        my $sth =
          $dbh->prepare(
"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"
              );
            $authvalsth->execute($authorisedvaluecat);
            while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
                $itemstatus{$authorisedvalue} = $lib;
            }
            $authvalsth->finish;
            return \%itemstatus;
            exit 1;
        }
        else {

            #No authvalue list
            # build default
        }
        $sth->finish;
    }

    #No authvalue list
    #build default
    $itemstatus{"1"} = "Not For Loan";
    return \%itemstatus;
}

=head2 getitemlocation

  $itemlochash = &getitemlocation($fwk);
  returns informations about location.
  where fwk stands for an optional framework code.
  Create a location selector with the following code

=head3 in PERL SCRIPT

my $itemlochash = getitemlocation;
my @itemlocloop;
foreach my $thisloc (keys %$itemlochash) {
    my $selected = 1 if $thisbranch eq $branch;
    my %row =(locval => $thisloc,
                selected => $selected,
                locname => $itemlochash->{$thisloc},
            );
    push @itemlocloop, \%row;
}
$template->param(itemlocationloop => \@itemlocloop);

=head3 in TEMPLATE  
            <select name="location">
                <option value="">Default</option>
            <!-- TMPL_LOOP name="itemlocationloop" -->
                <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
            <!-- /TMPL_LOOP -->
            </select>

=cut

sub GetItemLocation {

    # returns a reference to a hash of references to location...
    my ($fwk) = @_;
    my %itemlocation;
    my $dbh = C4::Context->dbh;
    my $sth;
    $fwk = '' unless ($fwk);
    my ( $tag, $subfield ) =
      MARCfind_marc_from_kohafield( $dbh, "items.location", $fwk );
    if ( $tag and $subfield ) {
        my $sth =
          $dbh->prepare(
"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"
              );
            $authvalsth->execute($authorisedvaluecat);
            while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
                $itemlocation{$authorisedvalue} = $lib;
            }
            $authvalsth->finish;
            return \%itemlocation;
            exit 1;
        }
        else {

            #No authvalue list
            # build default
        }
        $sth->finish;
    }

    #No authvalue list
    #build default
    $itemlocation{"1"} = "Not For Loan";
    return \%itemlocation;
}

=head2 &GetBiblioItemData

  $itemdata = &GetBiblioItemData($biblioitemnumber);

Looks up the biblioitem with the given biblioitemnumber. Returns a
reference-to-hash. The keys are the fields from the C<biblio>,
C<biblioitems>, and C<itemtypes> tables in the Koha database, except
that C<biblioitems.notes> is given as C<$itemdata-E<gt>{bnotes}>.

=cut

#'
sub GetBiblioItemData {
    my ($bibitem) = @_;
    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 $data;

    $sth->execute($bibitem);

    $data = $sth->fetchrow_hashref;

    $sth->finish;
    return ($data);
}    # sub &GetBiblioItemData

=head2 GetItemFromBarcode

$result = GetItemFromBarcode($barcode);

=cut

sub GetItemFromBarcode {
    my ($barcode) = @_;
    my $dbh = C4::Context->dbh;

    my $rq =
      $dbh->prepare("SELECT itemnumber from items where items.barcode=?");
    $rq->execute($barcode);
    my ($result) = $rq->fetchrow;
    return ($result);
}

=head2 GetBiblioItemByBiblioNumber

NOTE : This function has been copy/paste from C4/Biblio.pm from head before zebra integration.

=cut

sub GetBiblioItemByBiblioNumber {
    my ($biblionumber) = @_;
    my $dbh = C4::Context->dbh;
    my $sth = $dbh->prepare("Select * from biblioitems where biblionumber = ?");
    my $count = 0;
    my @results;

    $sth->execute($biblionumber);

    while ( my $data = $sth->fetchrow_hashref ) {
        push @results, $data;
    }

    $sth->finish;
    return @results;
}

=head2 GetBiblioFromItemNumber

  $item = &GetBiblioFromItemNumber($itemnumber);

Looks up the item with the given itemnumber.

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
database.

=cut

#'
sub GetBiblioFromItemNumber {
    my ( $itemnumber ) = @_;
    my $dbh = C4::Context->dbh;
    my $env;
    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 $data = $sth->fetchrow_hashref;
    $sth->finish;
    return ($data);
}

=head2 GetBiblio

( $count, @results ) = &GetBiblio($biblionumber);

=cut

sub GetBiblio {
    my ($biblionumber) = @_;
    my $dbh = C4::Context->dbh;
    my $sth = $dbh->prepare("Select * from biblio where biblionumber = ?");
    my $count = 0;
    my @results;
    $sth->execute($biblionumber);
    while ( my $data = $sth->fetchrow_hashref ) {
        $results[$count] = $data;
        $count++;
    }    # while
    $sth->finish;
    return ( $count, @results );
}    # sub GetBiblio

=head2 getitemsbybiblioitem

( $count, @results ) = &getitemsbybiblioitem($biblioitemnum);

=cut

sub getitemsbybiblioitem {
    my ($biblioitemnum) = @_;
    my $dbh             = C4::Context->dbh;
    my $sth             = $dbh->prepare(
        "Select * from items, biblio where
biblio.biblionumber = items.biblionumber and biblioitemnumber
= ?"
    );

    # || die "Cannot prepare $query\n" . $dbh->errstr;
    my $count = 0;
    my @results;

    $sth->execute($biblioitemnum);

    # || die "Cannot execute $query\n" . $sth->errstr;
    while ( my $data = $sth->fetchrow_hashref ) {
        $results[$count] = $data;
        $count++;
    }    # while

    $sth->finish;
    return ( $count, @results );
}    # sub getitemsbybiblioitem

=head2 get_itemnumbers_of

  my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);

Given a list of biblionumbers, return the list of corresponding itemnumbers
for each biblionumber.

Return a reference on a hash where keys are biblionumbers and values are
references on array of itemnumbers.

=cut

sub get_itemnumbers_of {
    my @biblionumbers = @_;

    my $dbh = C4::Context->dbh;

    my $query = '
        SELECT itemnumber,
            biblionumber
        FROM items
        WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
    ';
    my $sth = $dbh->prepare($query);
    $sth->execute(@biblionumbers);

    my %itemnumbers_of;

    while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
        push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
    }

    return \%itemnumbers_of;
}

=head2 getRecord

$record = getRecord( $server, $koha_query, $recordSyntax );

get a single record in piggyback mode from Zebra and return it in the requested record syntax

default record syntax is XML

=cut

sub getRecord {
    my ( $server, $koha_query, $recordSyntax ) = @_;
    $recordSyntax = "xml" unless $recordSyntax;
    my $Zconn = C4::Context->Zconn( $server, 0, 1, 1, $recordSyntax );
    my $rs = $Zconn->search( new ZOOM::Query::CCL2RPN( $koha_query, $Zconn ) );
    if ( $rs->record(0) ) {
        return $rs->record(0)->raw();
    }
}

=head2 GetItemInfosOf

GetItemInfosOf(@itemnumbers);

=cut

sub GetItemInfosOf {
    my @itemnumbers = @_;

    my $query = '
        SELECT *
        FROM items
        WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
    ';
    return get_infos_of( $query, 'itemnumber' );
}

=head2 GetBiblioItemInfosOf

GetBiblioItemInfosOf(@biblioitemnumbers);

=cut

sub GetBiblioItemInfosOf {
    my @biblioitemnumbers = @_;

    my $query = '
        SELECT biblioitemnumber,
            publicationyear,
            itemtype
        FROM biblioitems
        WHERE biblioitemnumber IN (' . join( ',', @biblioitemnumbers ) . ')
    ';
    return get_infos_of( $query, 'biblioitemnumber' );
}

=head2 z3950_extended_services

z3950_extended_services($serviceType,$serviceOptions,$record);

    z3950_extended_services is used to handle all interactions with Zebra's extended serices package, which is employed to perform all management of the MARC data stored in Zebra.

C<$serviceType> one of: itemorder,create,drop,commit,update,xmlupdate

C<$serviceOptions> a has of key/value pairs. For instance, if service_type is 'update', $service_options should contain:

    action => update action, one of specialUpdate, recordInsert, recordReplace, recordDelete, elementUpdate.

and maybe

    recordidOpaque => Opaque Record ID (user supplied) or recordidNumber => Record ID number (system number).
    syntax => the record syntax (transfer syntax)
    databaseName = Database from connection object

    To set serviceOptions, call set_service_options($serviceType)

C<$record> the record, if one is needed for the service type

    A record should be in XML. You can convert it to XML from MARC by running it through marc2xml().

=cut

sub z3950_extended_services {
    my ( $server, $serviceType, $action, $serviceOptions ) = @_;

    # get our connection object
    my $Zconn = C4::Context->Zconn( $server, 0, 1 );

    # create a new package object
    my $Zpackage = $Zconn->package();

    # set our options
    $Zpackage->option( action => $action );

    if ( $serviceOptions->{'databaseName'} ) {
        $Zpackage->option( databaseName => $serviceOptions->{'databaseName'} );
    }
    if ( $serviceOptions->{'recordIdNumber'} ) {
        $Zpackage->option(
            recordIdNumber => $serviceOptions->{'recordIdNumber'} );
    }
    if ( $serviceOptions->{'recordIdOpaque'} ) {
        $Zpackage->option(
            recordIdOpaque => $serviceOptions->{'recordIdOpaque'} );
    }

 # this is an ILL request (Zebra doesn't support it, but Koha could eventually)
 #if ($serviceType eq 'itemorder') {
 #   $Zpackage->option('contact-name' => $serviceOptions->{'contact-name'});
 #   $Zpackage->option('contact-phone' => $serviceOptions->{'contact-phone'});
 #   $Zpackage->option('contact-email' => $serviceOptions->{'contact-email'});
 #   $Zpackage->option('itemorder-item' => $serviceOptions->{'itemorder-item'});
 #}

    if ( $serviceOptions->{record} ) {
        $Zpackage->option( record => $serviceOptions->{record} );

        # can be xml or marc
        if ( $serviceOptions->{'syntax'} ) {
            $Zpackage->option( syntax => $serviceOptions->{'syntax'} );
        }
    }

    # send the request, handle any exception encountered
    eval { $Zpackage->send($serviceType) };
    if ( $@ && $@->isa("ZOOM::Exception") ) {
        return "error:  " . $@->code() . " " . $@->message() . "\n";
    }

    # free up package resources
    $Zpackage->destroy();
}

=head2 set_service_options

my $serviceOptions = set_service_options($serviceType);

C<$serviceType> itemorder,create,drop,commit,update,xmlupdate

Currently, we only support 'create', 'commit', and 'update'. 'drop' support will be added as soon as Zebra supports it.

=cut

sub set_service_options {
    my ($serviceType) = @_;
    my $serviceOptions;

# FIXME: This needs to be an OID ... if we ever need 'syntax' this sub will need to change
#   $serviceOptions->{ 'syntax' } = ''; #zebra doesn't support syntaxes other than xml

    if ( $serviceType eq 'commit' ) {

        # nothing to do
    }
    if ( $serviceType eq 'create' ) {

        # nothing to do
    }
    if ( $serviceType eq 'drop' ) {
        die "ERROR: 'drop' not currently supported (by Zebra)";
    }
    return $serviceOptions;
}

=head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT

=head2 MARCgettagslib

=cut

sub MARCgettagslib {
    my ( $dbh, $forlibrarian, $frameworkcode ) = @_;
    $frameworkcode = "" unless $frameworkcode;
    my $sth;
    my $libfield = ( $forlibrarian eq 1 ) ? 'liblibrarian' : 'libopac';

    # check that framework exists
    $sth =
      $dbh->prepare(
        "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"
      );
    $sth->execute($frameworkcode);
    my ( $liblibrarian, $libopac, $tag, $res, $tab, $mandatory, $repeatable );

    while ( ( $tag, $liblibrarian, $libopac, $mandatory, $repeatable ) =
        $sth->fetchrow )
    {
        $res->{$tag}->{lib} =
          ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
        $res->{$tab}->{tab}        = "";            # XXX
        $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 from marc_subfield_structure where frameworkcode=? order by tagfield,tagsubfield"
      );
    $sth->execute($frameworkcode);

    my $subfield;
    my $authorised_value;
    my $authtypecode;
    my $value_builder;
    my $kohafield;
    my $seealso;
    my $hidden;
    my $isurl;
    my $link;

    while (
        (
            $tag,          $subfield,      $liblibrarian,
            ,              $libopac,       $tab,
            $mandatory,    $repeatable,    $authorised_value,
            $authtypecode, $value_builder, $kohafield,
            $seealso,      $hidden,        $isurl,
            $link
        )
        = $sth->fetchrow
      )
    {
        $res->{$tag}->{$subfield}->{lib} =
          ( $forlibrarian or !$libopac ) ? $liblibrarian : $libopac;
        $res->{$tag}->{$subfield}->{tab}              = $tab;
        $res->{$tag}->{$subfield}->{mandatory}        = $mandatory;
        $res->{$tag}->{$subfield}->{repeatable}       = $repeatable;
        $res->{$tag}->{$subfield}->{authorised_value} = $authorised_value;
        $res->{$tag}->{$subfield}->{authtypecode}     = $authtypecode;
        $res->{$tag}->{$subfield}->{value_builder}    = $value_builder;
        $res->{$tag}->{$subfield}->{kohafield}        = $kohafield;
        $res->{$tag}->{$subfield}->{seealso}          = $seealso;
        $res->{$tag}->{$subfield}->{hidden}           = $hidden;
        $res->{$tag}->{$subfield}->{isurl}            = $isurl;
        $res->{$tag}->{$subfield}->{link}             = $link;
    }
    return $res;
}

=head2 MARCfind_marc_from_kohafield

=cut

sub MARCfind_marc_from_kohafield {
    my ( $dbh, $kohafield, $frameworkcode ) = @_;
    return 0, 0 unless $kohafield;
    my $relations = C4::Context->marcfromkohafield;
    return (
        $relations->{$frameworkcode}->{$kohafield}->[0],
        $relations->{$frameworkcode}->{$kohafield}->[1]
    );
}

=head2 MARCaddbiblio

&MARCaddbiblio($newrec,$biblionumber,$frameworkcode);

Add MARC data for a biblio to koha 

=cut

sub MARCaddbiblio {

# pass the MARC::Record to this function, and it will create the records in the marc tables
    my ( $record, $biblionumber, $frameworkcode ) = @_;
    my $dbh = C4::Context->dbh;
    my @fields = $record->fields();
    if ( !$frameworkcode ) {
        $frameworkcode = "";
    }
    my $sth =
      $dbh->prepare("UPDATE biblio SET frameworkcode=? WHERE biblionumber=?");
    $sth->execute( $frameworkcode, $biblionumber );
    $sth->finish;
    my $encoding = C4::Context->preference("marcflavour");

# deal with UNIMARC field 100 (encoding) : create it if needed & set encoding to unicode
    if ( $encoding eq "UNIMARC" ) {
        my $string;
        if ( $record->subfield( 100, "a" ) ) {
            $string = $record->subfield( 100, "a" );
            my $f100 = $record->field(100);
            $record->delete_field($f100);
        }
        else {
            $string = POSIX::strftime( "%Y%m%d", localtime );
            $string =~ s/\-//g;
            $string = sprintf( "%-*s", 35, $string );
        }
        substr( $string, 22, 6, "frey50" );
        unless ( $record->subfield( 100, "a" ) ) {
            $record->insert_grouped_field(
                MARC::Field->new( 100, "", "", "a" => $string ) );
        }
    }
#     warn "biblionumber : ".$biblionumber;
    $sth =
      $dbh->prepare(
        "update biblioitems set marc=?,marcxml=?  where biblionumber=?");
    $sth->execute( $record->as_usmarc(), $record->as_xml_record(),
        $biblionumber );
#     warn $record->as_xml_record();
    $sth->finish;
    zebraop($biblionumber,"specialUpdate","biblioserver");
    return $biblionumber;
}

=head2 MARCadditem

$newbiblionumber = MARCadditem( $record, $biblionumber, $frameworkcode );

=cut

sub MARCadditem {

# 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);

    # 2nd recreate it
    my @fields = $record->fields();
    foreach my $field (@fields) {
        $newrec->append_fields($field);
    }

    # FIXME: should we be making sure the biblionumbers are the same?
    my $newbiblionumber =
      &MARCaddbiblio( $newrec, $biblionumber, $frameworkcode );
    return $newbiblionumber;
}

=head2 GetMarcBiblio

Returns MARC::Record of the biblionumber passed in parameter.

=cut

sub GetMarcBiblio {
    my $biblionumber = shift;
    my $dbh          = C4::Context->dbh;
    my $sth          =
      $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
    $sth->execute($biblionumber);
    my ($marcxml) = $sth->fetchrow;
#     warn "marcxml : $marcxml";
    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 $record = MARC::Record->new();
    $record = MARC::Record::new_from_xml( $marcxml, "utf8",C4::Context->preference('marcflavour')) if $marcxml;
    return $record;
}

=head2 GetXmlBiblio

my $marcxml = GetXmlBiblio($biblionumber);

Returns biblioitems.marcxml of the biblionumber passed in parameter.

=cut

sub GetXmlBiblio {
    my ( $biblionumber ) = @_;
    my $dbh = C4::Context->dbh;
    my $sth =
      $dbh->prepare("select marcxml from biblioitems where biblionumber=? ");
    $sth->execute($biblionumber);
    my ($marcxml) = $sth->fetchrow;
    return $marcxml;
}

=head2 GetAuthorisedValueDesc

my $subfieldvalue =get_authorised_value_desc(
    $tag, $subf[$i][0],$subf[$i][1], '', $taglib);

=cut

sub GetAuthorisedValueDesc {
    my ( $tag, $subfield, $value, $framework, $tagslib ) = @_;
    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);
    }

    #---- "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 = ?"
          );
        $sth->execute( $category, $value );
        my $data = $sth->fetchrow_hashref;
        return $data->{'lib'};
    }
    else {
        return $value;    # if nothing is found return the original value
    }
}

=head2 MARCgetitem

Returns MARC::Record of the item passed in parameter.

=cut

sub MARCgetitem {
    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();
#     warn "marcxml :$marcxml";
    $record = MARC::Record::new_from_xml( $marcxml, "utf8", $marcflavour );
#     warn "record :".$record->as_formatted;
    # now, find where the itemnumber is stored & extract only the item
    my ( $itemnumberfield, $itemnumbersubfield ) =
      MARCfind_marc_from_kohafield( $dbh, 'items.itemnumber', '' );
    my @fields = $record->field($itemnumberfield);
    foreach my $field (@fields) {
        if ( $field->subfield($itemnumbersubfield) eq $itemnumber ) {
            $newrecord->insert_fields_ordered($field);
        }
    }
    return $newrecord;
}

=head2 GetMarcNotes

$marcnotesarray = GetMarcNotes( $record, $marcflavour );

get a single record in piggyback mode from Zebra and return it in the requested record syntax

default record syntax is XML

=cut

sub GetMarcNotes {
    my ( $record, $marcflavour ) = @_;
    my $scope;
    if ( $marcflavour eq "MARC21" ) {
        $scope = '5..';
    }
    else {    # assume unimarc if not marc21
        $scope = '3..';
    }
    my @marcnotes;
    my $note = "";
    my $tag  = "";
    my $marcnote;
    foreach my $field ( $record->field($scope) ) {
        my $value = $field->as_string();
        if ( $note ne "" ) {
            $marcnote = { marcnote => $note, };
            push @marcnotes, $marcnote;
            $note = $value;
        }
        if ( $note ne $value ) {
            $note = $note . " " . $value;
        }
    }

    if ( $note ) {
        $marcnote = { marcnote => $note };
        push @marcnotes, $marcnote;    #load last tag into array
    }
    return \@marcnotes;
}    # end GetMarcNotes

=head2 GetMarcSubjects

$marcsubjcts = GetMarcSubjects($record,$marcflavour);

=cut

sub GetMarcSubjects {
    my ( $record, $marcflavour ) = @_;
    my ( $mintag, $maxtag );
    if ( $marcflavour eq "MARC21" ) {
        $mintag = "600";
        $maxtag = "699";
    }
    else {    # assume unimarc if not marc21
        $mintag = "600";
        $maxtag = "611";
    }

    my @marcsubjcts;

    foreach my $field ( $record->fields ) {
        next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
        my @subfields = $field->subfields();
        my $link;
        my $label = "su:";
        my $flag = 0;
        for my $subject_subfield ( @subfields ) {
            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:$//;
            }
        }
        $label =~ s/su/ /g;
        $label =~ s/://g;
        $label =~ s/-to//g;
        $label =~ s/and//g;
        push @marcsubjcts,
          {
            label => $label,
            link  => $link
          }
    }
    return \@marcsubjcts;
}    #end GetMarcSubjects

=head2 GetMarcAuthors

authors = GetMarcAuthors($record,$marcflavour);

=cut

sub GetMarcAuthors {
    my ( $record, $marcflavour ) = @_;
    my ( $mintag, $maxtag );
    if ( $marcflavour eq "MARC21" ) {
        $mintag = "100";
        $maxtag = "111"; 
    }
    else {    # assume unimarc if not marc21
        $mintag = "701";
        $maxtag = "712";
    }

    my @marcauthors;

    foreach my $field ( $record->fields ) {
        next unless $field->tag() >= $mintag && $field->tag() <= $maxtag;
        my %hash;
        my @subfields = $field->subfields();
        my $count_auth = 0;
        my $and ;
        for my $authors_subfield (@subfields) {
        	if ($count_auth ne '0'){
        	$and = " and au:";
        	}
            $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;
    }
    return \@marcauthors;
}

=head2 GetMarcSeries

$marcseriessarray = GetMarcSeries($record,$marcflavour);

=cut

sub GetMarcSeries {
    my ($record, $marcflavour) = @_;
    my ($mintag, $maxtag);
    if ($marcflavour eq "MARC21") {
        $mintag = "440";
        $maxtag = "490";
    } else {           # assume unimarc if not marc21
        $mintag = "600";
        $maxtag = "619";
    }

    my @marcseries;
    my $subjct = "";
    my $subfield = "";
    my $marcsubjct;

    foreach my $field ($record->field('440'), $record->field('490')) {
        my @subfields_loop;
        #my $value = $field->subfield('a');
        #$marcsubjct = {MARCSUBJCT => $value,};
        my @subfields = $field->subfields();
        #warn "subfields:".join " ", @$subfields;
        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 $code = $series_subfield->[0];
            my $value = $series_subfield->[1];
            my $linkvalue = $value;
            $linkvalue =~ s/(\(|\))//g;
            my $operator = " and " unless $counter==0;
            push @link_loop, {link => $linkvalue, operator => $operator };
            my $separator = C4::Context->preference("authoritysep") unless $counter==0;
			if ($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 };
        #$marcsubjct = {MARCSUBJCT => $field->as_string(),};
        #push @marcsubjcts, $marcsubjct;
        #$subjct = $value;

    }
    my $marcseriessarray=\@marcseries;
    return $marcseriessarray;
}  #end getMARCseriess

=head2 MARCmodbiblio

MARCmodbibio($dbh,$biblionumber,$record,$frameworkcode,1);

Modify a biblio record with the option to save items data

=cut

sub MARCmodbiblio {
    my ( $dbh, $biblionumber, $record, $frameworkcode, $keep_items ) = @_;

    # delete original record but save the items
    my $newrec = &MARCdelbiblio( $biblionumber, $keep_items );

    # recreate it and add the new fields
    my @fields = $record->fields();
    foreach my $field (@fields) {

        # this requires a more recent version of MARC::Record
        # but ensures the fields are in order
        $newrec->insert_fields_ordered($field);
    }

    # give back our old leader
    $newrec->leader( $record->leader() );

    # add the record back with the items info preserved
    &MARCaddbiblio( $newrec, $biblionumber, $frameworkcode );
}

=head2 MARCdelbiblio

&MARCdelbiblio( $biblionumber, $keep_items )

if the keep_item is set to 1, then all items are preserved.
This flag is set when the delbiblio is called by modbiblio
due to a too complex structure of MARC (repeatable fields and subfields),
the best solution for a modif is to delete / recreate the record.

1st of all, copy the MARC::Record to deletedbiblio table => if a true deletion, MARC data will be kept.
if deletion called before MARCmodbiblio => won't do anything, as the oldbiblionumber doesn't
exist in deletedbiblio table

=cut

sub MARCdelbiblio {
    my ( $biblionumber, $keep_items ) = @_;
    my $dbh = C4::Context->dbh;
    
    my $record          = GetMarcBiblio($biblionumber);
    my $oldbiblionumber = $biblionumber;
    my $copy2deleted    =
      $dbh->prepare("update deletedbiblio set marc=? where biblionumber=?");
    $copy2deleted->execute( $record->as_usmarc(), $oldbiblionumber );
    my @fields = $record->fields();

    # now, delete in MARC tables.
    if ( $keep_items eq 1 ) {
        #search item field code
        my $sth =
          $dbh->prepare(
"select tagfield from marc_subfield_structure where kohafield like 'items.%'"
          );
        $sth->execute;
        my $itemtag = $sth->fetchrow_hashref->{tagfield};

        foreach my $field (@fields) {

            if ( $field->tag() ne $itemtag ) {
                $record->delete_field($field);
            }    #if
        }    #foreach
    }
    else {
        foreach my $field (@fields) {

            $record->delete_field($field);
        }    #foreach
    }
    return $record;
}

=head2 MARCdelitem

MARCdelitem( $biblionumber, $itemnumber )

delete the item field from the MARC record for the itemnumber specified

=cut

sub MARCdelitem {
    my ( $biblionumber, $itemnumber ) = @_;
    my $dbh = C4::Context->dbh;
    
    # get the MARC record
    my $record = GetMarcBiblio($biblionumber);

    # backup the record
    my $copy2deleted =
      $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
    $copy2deleted->execute( $record->as_usmarc(), $itemnumber );

    #search item field code
    my $sth =
      $dbh->prepare(
"SELECT tagfield,tagsubfield FROM marc_subfield_structure WHERE kohafield LIKE 'items.itemnumber'"
      );
    $sth->execute;
    my ( $itemtag, $itemsubfield ) = $sth->fetchrow;
    my @fields = $record->field($itemtag);
    # delete the item specified
    foreach my $field (@fields) {
        if ( $field->subfield($itemsubfield) eq $itemnumber ) {
            $record->delete_field($field);
        }
    }
    return $record;
}

=head2 MARCmoditemonefield

&MARCmoditemonefield( $biblionumber, $itemnumber, $itemfield, $newvalue )

=cut

sub MARCmoditemonefield {
    my ( $biblionumber, $itemnumber, $itemfield, $newvalue ) = @_;
    my $dbh = C4::Context->dbh;
    if ( !defined $newvalue ) {
        $newvalue = "";
    }

    my $record = MARCgetitem( $biblionumber, $itemnumber );

    my $sth =
      $dbh->prepare(
"select tagfield,tagsubfield from marc_subfield_structure where kohafield=?"
      );
    my $tagfield;
    my $tagsubfield;
    $sth->execute($itemfield);
    if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
        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);
            &MARCmoditem( $record, $biblionumber, $itemnumber, 0 );
        }
    }
}

=head2 MARCmoditem

&MARCmoditem( $record, $biblionumber, $itemnumber, $frameworkcode, $delete )

=cut

sub MARCmoditem {
    my ( $record, $biblionumber, $itemnumber, $frameworkcode, $delete ) = @_;
    my $dbh = C4::Context->dbh;
    
    # delete this item from MARC
    my $newrec = &MARCdelitem( $biblionumber, $itemnumber );

    # 2nd recreate it
    my @fields = $record->fields();
    ###NEU specific add cataloguers cardnumber as well
    my $cardtag = C4::Context->preference('itemcataloguersubfield');

    foreach my $field (@fields) {
        if ($cardtag) {
            my $me = C4::Context->userenv;
            my $cataloguer = $me->{'cardnumber'} if ($me);
            $field->update( $cardtag => $cataloguer ) if ($me);
        }
        $newrec->append_fields($field);
    }
    &MARCaddbiblio( $newrec, $biblionumber, $frameworkcode );
}

=head2 MARCfind_frameworkcode

$frameworkcode = MARCfind_frameworkcode( $biblionumber )

=cut

sub MARCfind_frameworkcode {
    my ( $biblionumber ) = @_;
    my $dbh = C4::Context->dbh;
    my $sth =
      $dbh->prepare("select frameworkcode from biblio where biblionumber=?");
    $sth->execute($biblionumber);
    my ($frameworkcode) = $sth->fetchrow;
    return $frameworkcode;
}

=head2 Koha2Marc

$record = Koha2Marc( $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

=cut

sub Koha2Marc {

    my ( $hash ) = @_;
    my $dbh = C4::Context->dbh;
    my $sth =
    $dbh->prepare(
        "select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
    );
    my $record = MARC::Record->new();
    foreach (keys %{$hash}) {
        &MARCkoha2marcOnefield( $sth, $record, $_,
            $hash->{$_}, '' );
        }
    return $record;
}
        
=head2 MARCkoha2marcBiblio

$record = MARCkoha2marcBiblio( $biblionumber, $biblioitemnumber )

this function builds partial MARC::Record from the old koha-DB fields

=cut

sub MARCkoha2marcBiblio {

    my ( $biblionumber, $biblioitemnumber ) = @_;
    my $dbh = C4::Context->dbh;
    my $sth =
      $dbh->prepare(
"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
      );
    my $record = MARC::Record->new();

    #--- if biblionumber, then retrieve old-style koha data
    if ( $biblionumber > 0 ) {
        my $sth2 = $dbh->prepare(
"select biblionumber,author,title,unititle,notes,abstract,serial,seriestitle,copyrightdate,timestamp
        from biblio where biblionumber=?"
        );
        $sth2->execute($biblionumber);
        my $row = $sth2->fetchrow_hashref;
        my $code;
        foreach $code ( keys %$row ) {
            if ( $row->{$code} ) {
                &MARCkoha2marcOnefield( $sth, $record, "biblio." . $code,
                    $row->{$code}, '' );
            }
        }
    }

    #--- if biblioitem, then retrieve old-style koha data
    if ( $biblioitemnumber > 0 ) {
        my $sth2 = $dbh->prepare(
            " SELECT biblioitemnumber,biblionumber,volume,number,classification,
                        itemtype,url,isbn,issn,dewey,subclass,publicationyear,publishercode,
                        volumedate,volumeddesc,timestamp,illus,pages,notes AS bnotes,size,place
                    FROM biblioitems
                    WHERE biblioitemnumber=?
                    "
        );
        $sth2->execute($biblioitemnumber);
        my $row = $sth2->fetchrow_hashref;
        my $code;
        foreach $code ( keys %$row ) {
            if ( $row->{$code} ) {
                &MARCkoha2marcOnefield( $sth, $record, "biblioitems." . $code,
                    $row->{$code}, '' );
            }
        }
    }
    return $record;
}

=head2 MARCkoha2marcItem

$record = MARCkoha2marcItem( $dbh, $biblionumber, $itemnumber );

=cut

sub MARCkoha2marcItem {

    # this function builds partial MARC::Record from the old koha-DB fields
    my ( $dbh, $biblionumber, $itemnumber ) = @_;

    #    my $dbh=&C4Connect;
    my $sth =
      $dbh->prepare(
"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
      );
    my $record = MARC::Record->new();

    #--- if item, then retrieve old-style koha data
    if ( $itemnumber > 0 ) {

        #    print STDERR "prepare $biblionumber,$itemnumber\n";
        my $sth2 = $dbh->prepare(
"SELECT itemnumber,biblionumber,multivolumepart,biblioitemnumber,barcode,dateaccessioned,
                        booksellerid,homebranch,price,replacementprice,replacementpricedate,datelastborrowed,
                        datelastseen,multivolume,stack,notforloan,itemlost,wthdrawn,itemcallnumber,issues,renewals,
                    reserves,restricted,binding,itemnotes,holdingbranch,timestamp,onloan,Cutterextra
                    FROM items
                    WHERE itemnumber=?"
        );
        $sth2->execute($itemnumber);
        my $row = $sth2->fetchrow_hashref;
        my $code;
        foreach $code ( keys %$row ) {
            if ( $row->{$code} ) {
                &MARCkoha2marcOnefield( $sth, $record, "items." . $code,
                    $row->{$code}, '' );
            }
        }
    }
    return $record;
}

=head2 MARCkoha2marcOnefield

$record = MARCkoha2marcOnefield( $sth, $record, $kohafieldname, $value, $frameworkcode );

=cut

sub MARCkoha2marcOnefield {
    my ( $sth, $record, $kohafieldname, $value, $frameworkcode ) = @_;
    $frameworkcode='' unless $frameworkcode;
    my $tagfield;
    my $tagsubfield;

    if ( !defined $sth ) {
        my $dbh = C4::Context->dbh;
        $sth =
          $dbh->prepare(
"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
          );
    }
    $sth->execute( $frameworkcode, $kohafieldname );
    if ( ( $tagfield, $tagsubfield ) = $sth->fetchrow ) {
        my $tag = $record->field($tagfield);
        if ($tag) {
            $tag->update( $tagsubfield => $value );
            $record->delete_field($tag);
            $record->insert_fields_ordered($tag);
        }
        else {
            $record->add_fields( $tagfield, " ", " ", $tagsubfield => $value );
        }
    }
    return $record;
}

=head2 MARChtml2xml

$xml = MARChtml2xml( $tags, $subfields, $values, $indicator, $ind_tag )

=cut

sub MARChtml2xml {
    my ( $tags, $subfields, $values, $indicator, $ind_tag ) = @_;
    my $xml = MARC::File::XML::header('UTF-8');
    if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
        MARC::File::XML->default_record_format('UNIMARC');
        use POSIX qw(strftime);
        my $string = strftime( "%Y%m%d", localtime(time) );
        $string = sprintf( "%-*s", 35, $string );
        substr( $string, 22, 6, "frey50" );
        $xml .= "<datafield tag=\"100\" ind1=\"\" ind2=\"\">\n";
        $xml .= "<subfield code=\"a\">$string</subfield>\n";
        $xml .= "</datafield>\n";
    }
    my $prevvalue;
    my $prevtag = -1;
    my $first   = 1;
    my $j       = -1;
    for ( my $i = 0 ; $i <= @$tags ; $i++ ) {
        @$values[$i] =~ s/&/&amp;/g;
        @$values[$i] =~ s/</&lt;/g;
        @$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 ( ( @$tags[$i] ne $prevtag ) ) {
            $j++ unless ( @$tags[$i] eq "" );
            if ( !$first ) {
                $xml .= "</datafield>\n";
                if (   ( @$tags[$i] && @$tags[$i] > 10 )
                    && ( @$values[$i] ne "" ) )
                {
                    my $ind1 = substr( @$indicator[$j], 0, 1 );
                    my $ind2;
                    if ( @$indicator[$j] ) {
                        $ind2 = substr( @$indicator[$j], 1, 1 );
                    }
                    else {
                        warn "Indicator in @$tags[$i] is empty";
                        $ind2 = " ";
                    }
                    $xml .=
"<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
                    $xml .=
"<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
                    $first = 0;
                }
                else {
                    $first = 1;
                }
            }
            else {
                if ( @$values[$i] ne "" ) {

                    # leader
                    if ( @$tags[$i] eq "000" ) {
                        $xml .= "<leader>@$values[$i]</leader>\n";
                        $first = 1;

                        # rest of the fixed fields
                    }
                    elsif ( @$tags[$i] < 10 ) {
                        $xml .=
"<controlfield tag=\"@$tags[$i]\">@$values[$i]</controlfield>\n";
                        $first = 1;
                    }
                    else {
                        my $ind1 = substr( @$indicator[$j], 0, 1 );
                        my $ind2 = substr( @$indicator[$j], 1, 1 );
                        $xml .=
"<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
                        $xml .=
"<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
                        $first = 0;
                    }
                }
            }
        }
        else {    # @$tags[$i] eq $prevtag
            if ( @$values[$i] eq "" ) {
            }
            else {
                if ($first) {
                    my $ind1 = substr( @$indicator[$j], 0, 1 );
                    my $ind2 = substr( @$indicator[$j], 1, 1 );
                    $xml .=
"<datafield tag=\"@$tags[$i]\" ind1=\"$ind1\" ind2=\"$ind2\">\n";
                    $first = 0;
                }
                $xml .=
"<subfield code=\"@$subfields[$i]\">@$values[$i]</subfield>\n";
            }
        }
        $prevtag = @$tags[$i];
    }
    $xml .= MARC::File::XML::footer();

    return $xml;
}

=head2 MARChtml2marc

$record = MARChtml2marc( $dbh, $rtags, $rsubfields, $rvalues, %indicators )

=cut

sub MARChtml2marc {
    my ( $dbh, $rtags, $rsubfields, $rvalues, %indicators ) = @_;
    my $prevtag = -1;
    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]
                );
            }
            $prevtag = @$rtags[$i];
        }
        else {
            if ( @$rtags[$i] < 10 ) {
                $prevvalue = @$rvalues[$i];
            }
            else {
                if ( length( @$rvalues[$i] ) > 0 ) {
                    $field->add_subfields( @$rsubfields[$i] => @$rvalues[$i] );
                }
            }
            $prevtag = @$rtags[$i];
        }
    }

    # the last has not been included inside the loop... do it now !
    $record->insert_fields_ordered($field) if $field;

    #     warn "HTML2MARC=".$record->as_formatted;
    $record->encoding('UTF-8');

    #    $record->MARC::File::USMARC::update_leader();
    return $record;
}

=head2 MARCmarc2koha

$result = MARCmarc2koha( $dbh, $record, $frameworkcode )

=cut

sub MARCmarc2koha {
    my ( $dbh, $record, $frameworkcode ) = @_;
    my $sth =
      $dbh->prepare(
"select tagfield,tagsubfield from marc_subfield_structure where frameworkcode=? and kohafield=?"
      );
    my $result;
    my $sth2 = $dbh->prepare("SHOW COLUMNS from biblio");
    $sth2->execute;
    my $field;
    while ( ($field) = $sth2->fetchrow ) {
        $result =
          &MARCmarc2kohaOneField( "biblio", $field, $record, $result,
            $frameworkcode );
    }
    $sth2 = $dbh->prepare("SHOW COLUMNS from biblioitems");
    $sth2->execute;
    while ( ($field) = $sth2->fetchrow ) {
        if ( $field eq 'notes' ) { $field = 'bnotes'; }
        $result =
          &MARCmarc2kohaOneField( "biblioitems", $field, $record, $result,
            $frameworkcode );
    }
    $sth2 = $dbh->prepare("SHOW COLUMNS from items");
    $sth2->execute;
    while ( ($field) = $sth2->fetchrow ) {
        $result =
          &MARCmarc2kohaOneField( "items", $field, $record, $result,
            $frameworkcode );
    }

    #
    # 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;
}

=head2 MARCmarc2kohaOneField

$result = MARCmarc2kohaOneField( $kohatable, $kohafield, $record, $result, $frameworkcode )

=cut

sub MARCmarc2kohaOneField {

# 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 ) =
      MARCfind_marc_from_kohafield( "", $kohatable . "." . $kohafield,
        $frameworkcode );
    foreach my $field ( $record->field($tagfield) ) {
        if ( $field->tag() < 10 ) {
            if ( $result->{$kohafield} ) {
                $result->{$kohafield} .= " | " . $field->data();
            }
            else {
                $result->{$kohafield} = $field->data();
            }
        }
        else {
            if ( $field->subfields ) {
                my @subfields = $field->subfields();
                foreach my $subfieldcount ( 0 .. $#subfields ) {
                    if ( $subfields[$subfieldcount][0] eq $subfield ) {
                        if ( $result->{$kohafield} ) {
                            $result->{$kohafield} .=
                              " | " . $subfields[$subfieldcount][1];
                        }
                        else {
                            $result->{$kohafield} =
                              $subfields[$subfieldcount][1];
                        }
                    }
                }
            }
        }
    }
    return $result;
}

=head2 MARCitemchange

&MARCitemchange( $record, $itemfield, $newvalue )

=cut

sub MARCitemchange {
    my ( $record, $itemfield, $newvalue ) = @_;
    my $dbh = C4::Context->dbh;
    
    my ( $tagfield, $tagsubfield ) =
      MARCfind_marc_from_kohafield( $dbh, $itemfield, "" );
    if ( ($tagfield) && ($tagsubfield) ) {
        my $tag = $record->field($tagfield);
        if ($tag) {
            $tag->update( $tagsubfield => $newvalue );
            $record->delete_field($tag);
            $record->insert_fields_ordered($tag);
        }
    }
}

=head1 INTERNAL FUNCTIONS

=head2 _koha_add_biblio

_koha_add_biblio($dbh,$biblioitem);

Internal function to add a biblio ($biblio is a hash with the values)

=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

    ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);

Find the given $subfield in the given $tag in the given
MARC::Record $record.  If the subfield is found, returns
the (indicators, value) pair; otherwise, (undef, undef) is
returned.

PROPOSITION :
Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
I suggest we export it from this module.

=cut

sub _find_value {
    my ( $tagfield, $insubfield, $record, $encoding ) = @_;
    my @result;
    my $indicator;
    if ( $tagfield < 10 ) {
        if ( $record->field($tagfield) ) {
            push @result, $record->field($tagfield)->data();
        }
        else {
            push @result, "";
        }
    }
    else {
        foreach my $field ( $record->field($tagfield) ) {
            my @subfields = $field->subfields();
            foreach my $subfield (@subfields) {
                if ( @$subfield[0] eq $insubfield ) {
                    push @result, @$subfield[1];
                    $indicator = $field->indicator(1) . $field->indicator(2);
                }
            }
        }
    }
    return ( $indicator, @result );
}

=head2 _koha_modify_biblio

Internal function for updating the biblio table

=cut

sub _koha_modify_biblio {
    my ( $dbh, $biblio ) = @_;

# 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 = ?"
      );
    $sth->execute(
        $biblio->{'title'},       $biblio->{'author'},
        $biblio->{'abstract'},    $biblio->{'copyrightdate'},
        $biblio->{'seriestitle'}, $biblio->{'serial'},
        $biblio->{'unititle'},    $biblio->{'notes'},
        $biblio->{'biblionumber'}
    );
    $sth->finish;
    return ( $biblio->{'biblionumber'} );
}

=head2 _koha_modify_biblioitem

_koha_modify_biblioitem( $dbh, $biblioitem );

=cut

sub _koha_modify_biblioitem {
    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);
    if ( $dbh->errstr ) {
        warn "$query";
    }
}

=head2 _koha_modify_note

_koha_modify_note( $dbh, $bibitemnum, $note );

=cut

sub _koha_modify_note {
    my ( $dbh, $bibitemnum, $note ) = @_;

    #  my $dbh=C4Connect;
    my $query = "update biblioitems set notes='$note' where
  biblioitemnumber='$bibitemnum'";
    my $sth = $dbh->prepare($query);
    $sth->execute;
    $sth->finish;
}

=head2 _koha_add_biblioitem

_koha_add_biblioitem( $dbh, $biblioitem );

Internal function to add a biblioitem

=cut

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'};
    $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'}
    );
    $sth->finish;
    return ($bibitemnum);
}

=head2 _koha_new_items

_koha_new_items( $dbh, $item, $barcode );

=cut

sub _koha_new_items {
    my ( $dbh, $item, $barcode ) = @_;

    #  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'};
    }

    # 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
        );
    }
    if ( defined $sth->errstr ) {
        $error .= $sth->errstr;
    }
    return ( $itemnumber, $error );
}

=head2 _koha_modify_item

_koha_modify_item( $dbh, $item, $op );

=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};
        }
    }
    $query .= " where itemnumber=?";
    push @bind, $item->{'itemnum'};
    if ( $item->{'replacement'} ne '' ) {
        $query =~ s/ where/,replacementprice='$item->{'replacement'}' where/;
    }
    my $sth = $dbh->prepare($query);
    $sth->execute(@bind);
    $sth->finish;
}

=head2 _koha_delete_item

_koha_delete_item( $dbh, $itemnum );

Internal function to delete an item record from the koha tables

=cut

sub _koha_delete_item {
    my ( $dbh, $itemnum ) = @_;

    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 @bind  = ();
    foreach my $temp ( keys %$data ) {
        $query .= "$temp = ?,";
        push( @bind, $data->{$temp} );
    }
    $query =~ s/\,$//;

    #  print $query;
    $sth = $dbh->prepare($query);
    $sth->execute(@bind);
    $sth->finish;
    $sth = $dbh->prepare("Delete from items where itemnumber=?");
    $sth->execute($itemnum);
    $sth->finish;
}

=head2 _koha_delete_biblio

$error = _koha_delete_biblio($dbh,$biblionumber);

Internal sub for deleting from biblio table -- also saves to deletedbiblio

C<$dbh> - the database handle
C<$biblionumber> - the biblionumber of the biblio to be deleted

=cut

# FIXME: add error handling

sub _koha_delete_biblio {
    my ( $dbh, $biblionumber ) = @_;

    # get all the data for this biblio
    my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
    $sth->execute($biblionumber);

    if ( my $data = $sth->fetchrow_hashref ) {

        # save the record in deletedbiblio
        # find the fields to save
        my $query = "INSERT INTO deletedbiblio SET ";
        my @bind  = ();
        foreach my $temp ( keys %$data ) {
            $query .= "$temp = ?,";
            push( @bind, $data->{$temp} );
        }

        # replace the last , by ",?)"
        $query =~ s/\,$//;
        my $bkup_sth = $dbh->prepare($query);
        $bkup_sth->execute(@bind);
        $bkup_sth->finish;

        # delete the biblio
        my $del_sth = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
        $del_sth->execute($biblionumber);
        $del_sth->finish;
    }
    $sth->finish;
    return undef;
}

=head2 _koha_delete_biblioitems

$error = _koha_delete_biblioitems($dbh,$biblioitemnumber);

Internal sub for deleting from biblioitems table -- also saves to deletedbiblioitems

C<$dbh> - the database handle
C<$biblionumber> - the biblioitemnumber of the biblioitem to be deleted

=cut

# FIXME: add error handling

sub _koha_delete_biblioitems {
    my ( $dbh, $biblioitemnumber ) = @_;

    # get all the data for this biblioitem
    my $sth =
      $dbh->prepare("SELECT * FROM biblioitems WHERE biblioitemnumber=?");
    $sth->execute($biblioitemnumber);

    if ( my $data = $sth->fetchrow_hashref ) {

        # save the record in deletedbiblioitems
        # find the fields to save
        my $query = "INSERT INTO deletedbiblioitems SET ";
        my @bind  = ();
        foreach my $temp ( keys %$data ) {
            $query .= "$temp = ?,";
            push( @bind, $data->{$temp} );
        }

        # replace the last , by ",?)"
        $query =~ s/\,$//;
        my $bkup_sth = $dbh->prepare($query);
        $bkup_sth->execute(@bind);
        $bkup_sth->finish;

        # delete the biblioitem
        my $del_sth =
          $dbh->prepare("DELETE FROM biblioitems WHERE biblioitemnumber=?");
        $del_sth->execute($biblioitemnumber);
        $del_sth->finish;
    }
    $sth->finish;
    return undef;
}

=head2 _koha_delete_items

$error = _koha_delete_items($dbh,$itemnumber);

Internal sub for deleting from items table -- also saves to deleteditems

C<$dbh> - the database handle
C<$itemnumber> - the itemnumber of the item to be deleted

=cut

# FIXME: add error handling

sub _koha_delete_items {
    my ( $dbh, $itemnumber ) = @_;

    # get all the data for this item
    my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
    $sth->execute($itemnumber);

    if ( my $data = $sth->fetchrow_hashref ) {

        # save the record in deleteditems
        # find the fields to save
        my $query = "INSERT INTO deleteditems SET ";
        my @bind  = ();
        foreach my $temp ( keys %$data ) {
            $query .= "$temp = ?,";
            push( @bind, $data->{$temp} );
        }

        # replace the last , by ",?)"
        $query =~ s/\,$//;
        my $bkup_sth = $dbh->prepare($query);
        $bkup_sth->execute(@bind);
        $bkup_sth->finish;

        # delete the item
        my $del_sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
        $del_sth->execute($itemnumber);
        $del_sth->finish;
    }
    $sth->finish;
    return undef;
}



=head2 modbiblio

  $biblionumber = &modbiblio($biblio);

Update a biblio record.

C<$biblio> is a reference-to-hash whose keys are the fields in the
biblio table in the Koha database. All fields must be present, not
just the ones you wish to change.

C<&modbiblio> updates the record defined by
C<$biblio-E<gt>{biblionumber}> with the values in C<$biblio>.

C<&modbiblio> returns C<$biblio-E<gt>{biblionumber}> whether it was
successful or not.

=cut

sub modbiblio {
    my ($biblio) = @_;
    my $dbh = C4::Context->dbh;
    my $biblionumber = _koha_modify_biblio( $dbh, $biblio );
    my $record = MARCkoha2marcBiblio( $biblionumber, $biblionumber );
    MARCmodbiblio( $dbh, $biblionumber, $record, "", 0 );
    return ($biblionumber);
}    # sub modbiblio

=head2 modbibitem

&modbibitem($biblioitem)

=cut

sub modbibitem {
    my ($biblioitem) = @_;
    my $dbh = C4::Context->dbh;
    &_koha_modify_biblio( $dbh, $biblioitem );
}    # sub modbibitem


=head2 newitems

$errors = &newitems( $item, @barcodes );

=cut

sub newitems {
    my ( $item, @barcodes ) = @_;
    my $dbh = C4::Context->dbh;
    my $errors;
    my $itemnumber;
    my $error;
    foreach my $barcode (@barcodes) {
        ( $itemnumber, $error ) = &_koha_new_items( $dbh, $item, uc($barcode) );
        $errors .= $error;
        my $MARCitem =
          &MARCkoha2marcItem( $dbh, $item->{biblionumber}, $itemnumber );
        &MARCadditem( $MARCitem, $item->{biblionumber} );
    }
    return ($errors);
}

=head2 moditem

$errors = &moditem( $item, $op );

=cut

sub moditem {
    my ( $item, $op ) = @_;
    my $dbh = C4::Context->dbh;
    &_koha_modify_item( $dbh, $item, $op );

    # if we're just setting statuses, just update items table
    # it's faster and zebra and marc will be synched anyway by the cron job
    unless ( $op eq "setstatus" ) {
        my $MARCitem = &MARCkoha2marcItem( $dbh, $item->{'biblionumber'},
            $item->{'itemnum'} );
        &MARCmoditem( $MARCitem, $item->{biblionumber}, $item->{itemnum},
                      MARCfind_frameworkcode( $item->{biblionumber} ), 0 );
    }
}

=head2 checkitems

$errors = &checkitems( $count, @barcodes );

=cut

sub checkitems {
    my ( $count, @barcodes ) = @_;
    my $dbh = C4::Context->dbh;
    my $error;
    my $sth = $dbh->prepare("Select * from items where barcode=?");
    for ( my $i = 0 ; $i < $count ; $i++ ) {
        $barcodes[$i] = uc $barcodes[$i];
        $sth->execute( $barcodes[$i] );
        if ( my $data = $sth->fetchrow_hashref ) {
            $error .= " Duplicate Barcode: $barcodes[$i]";
        }
    }
    $sth->finish;
    return ($error);
}

=head1  OTHER FUNCTIONS

=head2 char_decode

my $string = char_decode( $string, $encoding );

converts ISO 5426 coded string to UTF-8
sloppy code : should be improved in next issue

=cut

sub char_decode {
    my ( $string, $encoding ) = @_;
    $_ = $string;

    $encoding = C4::Context->preference("marcflavour") unless $encoding;
    if ( $encoding eq "UNIMARC" ) {

        #         s/\xe1/Æ/gm;
        s/\xe2/Ğ/gm;
        s/\xe9/Ø/gm;
        s/\xec/ş/gm;
        s/\xf1/æ/gm;
        s/\xf3/ğ/gm;
        s/\xf9/ø/gm;
        s/\xfb/ß/gm;
        s/\xc1\x61/à/gm;
        s/\xc1\x65/è/gm;
        s/\xc1\x69/ì/gm;
        s/\xc1\x6f/ò/gm;
        s/\xc1\x75/ù/gm;
        s/\xc1\x41/À/gm;
        s/\xc1\x45/È/gm;
        s/\xc1\x49/Ì/gm;
        s/\xc1\x4f/Ò/gm;
        s/\xc1\x55/Ù/gm;
        s/\xc2\x41/Á/gm;
        s/\xc2\x45/É/gm;
        s/\xc2\x49/Í/gm;
        s/\xc2\x4f/Ó/gm;
        s/\xc2\x55/Ú/gm;
        s/\xc2\x59/İ/gm;
        s/\xc2\x61/á/gm;
        s/\xc2\x65/é/gm;
        s/\xc2\x69/í/gm;
        s/\xc2\x6f/ó/gm;
        s/\xc2\x75/ú/gm;
        s/\xc2\x79/ı/gm;
        s/\xc3\x41/Â/gm;
        s/\xc3\x45/Ê/gm;
        s/\xc3\x49/Î/gm;
        s/\xc3\x4f/Ô/gm;
        s/\xc3\x55/Û/gm;
        s/\xc3\x61/â/gm;
        s/\xc3\x65/ê/gm;
        s/\xc3\x69/î/gm;
        s/\xc3\x6f/ô/gm;
        s/\xc3\x75/û/gm;
        s/\xc4\x41/Ã/gm;
        s/\xc4\x4e/Ñ/gm;
        s/\xc4\x4f/Õ/gm;
        s/\xc4\x61/ã/gm;
        s/\xc4\x6e/ñ/gm;
        s/\xc4\x6f/õ/gm;
        s/\xc8\x41/Ä/gm;
        s/\xc8\x45/Ë/gm;
        s/\xc8\x49/Ï/gm;
        s/\xc8\x61/ä/gm;
        s/\xc8\x65/ë/gm;
        s/\xc8\x69/ï/gm;
        s/\xc8\x6F/ö/gm;
        s/\xc8\x75/ü/gm;
        s/\xc8\x76/ÿ/gm;
        s/\xc9\x41/Ä/gm;
        s/\xc9\x45/Ë/gm;
        s/\xc9\x49/Ï/gm;
        s/\xc9\x4f/Ö/gm;
        s/\xc9\x55/Ü/gm;
        s/\xc9\x61/ä/gm;
        s/\xc9\x6f/ö/gm;
        s/\xc9\x75/ü/gm;
        s/\xca\x41/Å/gm;
        s/\xca\x61/å/gm;
        s/\xd0\x43/Ç/gm;
        s/\xd0\x63/ç/gm;

        # this handles non-sorting blocks (if implementation requires this)
        $string = nsb_clean($_);
    }
    elsif ( $encoding eq "USMARC" || $encoding eq "MARC21" ) {
        ##MARC-8 to UTF-8

        s/\xe1\x61/à/gm;
        s/\xe1\x65/è/gm;
        s/\xe1\x69/ì/gm;
        s/\xe1\x6f/ò/gm;
        s/\xe1\x75/ù/gm;
        s/\xe1\x41/À/gm;
        s/\xe1\x45/È/gm;
        s/\xe1\x49/Ì/gm;
        s/\xe1\x4f/Ò/gm;
        s/\xe1\x55/Ù/gm;
        s/\xe2\x41/Á/gm;
        s/\xe2\x45/É/gm;
        s/\xe2\x49/Í/gm;
        s/\xe2\x4f/Ó/gm;
        s/\xe2\x55/Ú/gm;
        s/\xe2\x59/İ/gm;
        s/\xe2\x61/á/gm;
        s/\xe2\x65/é/gm;
        s/\xe2\x69/í/gm;
        s/\xe2\x6f/ó/gm;
        s/\xe2\x75/ú/gm;
        s/\xe2\x79/ı/gm;
        s/\xe3\x41/Â/gm;
        s/\xe3\x45/Ê/gm;
        s/\xe3\x49/Î/gm;
        s/\xe3\x4f/Ô/gm;
        s/\xe3\x55/Û/gm;
        s/\xe3\x61/â/gm;
        s/\xe3\x65/ê/gm;
        s/\xe3\x69/î/gm;
        s/\xe3\x6f/ô/gm;
        s/\xe3\x75/û/gm;
        s/\xe4\x41/Ã/gm;
        s/\xe4\x4e/Ñ/gm;
        s/\xe4\x4f/Õ/gm;
        s/\xe4\x61/ã/gm;
        s/\xe4\x6e/ñ/gm;
        s/\xe4\x6f/õ/gm;
        s/\xe6\x41/Ă/gm;
        s/\xe6\x45/Ĕ/gm;
        s/\xe6\x65/ĕ/gm;
        s/\xe6\x61/ă/gm;
        s/\xe8\x45/Ë/gm;
        s/\xe8\x49/Ï/gm;
        s/\xe8\x65/ë/gm;
        s/\xe8\x69/ï/gm;
        s/\xe8\x76/ÿ/gm;
        s/\xe9\x41/A/gm;
        s/\xe9\x4f/O/gm;
        s/\xe9\x55/U/gm;
        s/\xe9\x61/a/gm;
        s/\xe9\x6f/o/gm;
        s/\xe9\x75/u/gm;
        s/\xea\x41/A/gm;
        s/\xea\x61/a/gm;

        #Additional Turkish characters
        s/\x1b//gm;
        s/\x1e//gm;
        s/(\xf0)s/\xc5\x9f/gm;
        s/(\xf0)S/\xc5\x9e/gm;
        s/(\xf0)c/ç/gm;
        s/(\xf0)C/Ç/gm;
        s/\xe7\x49/\\xc4\xb0/gm;
        s/(\xe6)G/\xc4\x9e/gm;
        s/(\xe6)g/ğ\xc4\x9f/gm;
        s/\xB8/ı/gm;
        s/\xB9/£/gm;
        s/(\xe8|\xc8)o/ö/gm;
        s/(\xe8|\xc8)O/Ö/gm;
        s/(\xe8|\xc8)u/ü/gm;
        s/(\xe8|\xc8)U/Ü/gm;
        s/\xc2\xb8/\xc4\xb1/gm;
        s/¸/\xc4\xb1/gm;

        # this handles non-sorting blocks (if implementation requires this)
        $string = nsb_clean($_);
    }
    return ($string);
}

=head2 PrepareItemrecordDisplay

PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber);

Returns a hash with all the fields for Display a given item data in a template

=cut

sub PrepareItemrecordDisplay {

    my ( $bibnum, $itemnum ) = @_;

    my $dbh = C4::Context->dbh;
    my $frameworkcode = &MARCfind_frameworkcode( $bibnum );
    my ( $itemtagfield, $itemtagsubfield ) =
      &MARCfind_marc_from_kohafield( $dbh, "items.itemnumber", $frameworkcode );
    my $tagslib = &MARCgettagslib( $dbh, 1, $frameworkcode );
    my $itemrecord = MARCgetitem( $bibnum, $itemnum) if ($itemnum);
    my @loop_data;
    my $authorised_values_sth =
      $dbh->prepare(
"select authorised_value,lib from authorised_values where category=? order by lib"
      );
    foreach my $tag ( sort keys %{$tagslib} ) {
        my $previous_tag = '';
        if ( $tag ne '' ) {
            # loop through each subfield
            my $cntsubf;
            foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
                next if ( subfield_is_koha_internal_p($subfield) );
                next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
                my %subfield_data;
                $subfield_data{tag}           = $tag;
                $subfield_data{subfield}      = $subfield;
                $subfield_data{countsubfield} = $cntsubf++;
                $subfield_data{kohafield}     =
                  $tagslib->{$tag}->{$subfield}->{'kohafield'};

         #        $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
                $subfield_data{marc_lib} =
                    "<span id=\"error\" title=\""
                  . $tagslib->{$tag}->{$subfield}->{lib} . "\">"
                  . substr( $tagslib->{$tag}->{$subfield}->{lib}, 0, 12 )
                  . "</span>";
                $subfield_data{mandatory} =
                  $tagslib->{$tag}->{$subfield}->{mandatory};
                $subfield_data{repeatable} =
                  $tagslib->{$tag}->{$subfield}->{repeatable};
                $subfield_data{hidden} = "display:none"
                  if $tagslib->{$tag}->{$subfield}->{hidden};
                my ( $x, $value );
                ( $x, $value ) = _find_value( $tag, $subfield, $itemrecord )
                  if ($itemrecord);
                $value =~ s/"/&quot;/g;

                # search for itemcallnumber if applicable
                if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq
                    'items.itemcallnumber'
                    && C4::Context->preference('itemcallnumber') )
                {
                    my $CNtag =
                      substr( C4::Context->preference('itemcallnumber'), 0, 3 );
                    my $CNsubfield =
                      substr( C4::Context->preference('itemcallnumber'), 3, 1 );
                    my $temp = $itemrecord->field($CNtag) if ($itemrecord);
                    if ($temp) {
                        $value = $temp->subfield($CNsubfield);
                    }
                }
                if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
                    my @authorised_values;
                    my %authorised_lib;

                    # builds list, depending on authorised value...
                    #---- branch
                    if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq
                        "branches" )
                    {
                        if ( ( C4::Context->preference("IndependantBranches") )
                            && ( C4::Context->userenv->{flags} != 1 ) )
                        {
                            my $sth =
                              $dbh->prepare(
"select branchcode,branchname from branches where branchcode = ? order by branchname"
                              );
                            $sth->execute( C4::Context->userenv->{branch} );
                            push @authorised_values, ""
                              unless (
                                $tagslib->{$tag}->{$subfield}->{mandatory} );
                            while ( my ( $branchcode, $branchname ) =
                                $sth->fetchrow_array )
                            {
                                push @authorised_values, $branchcode;
                                $authorised_lib{$branchcode} = $branchname;
                            }
                        }
                        else {
                            my $sth =
                              $dbh->prepare(
"select branchcode,branchname from branches order by branchname"
                              );
                            $sth->execute;
                            push @authorised_values, ""
                              unless (
                                $tagslib->{$tag}->{$subfield}->{mandatory} );
                            while ( my ( $branchcode, $branchname ) =
                                $sth->fetchrow_array )
                            {
                                push @authorised_values, $branchcode;
                                $authorised_lib{$branchcode} = $branchname;
                            }
                        }

                        #----- itemtypes
                    }
                    elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq
                        "itemtypes" )
                    {
                        my $sth =
                          $dbh->prepare(
"select itemtype,description from itemtypes order by description"
                          );
                        $sth->execute;
                        push @authorised_values, ""
                          unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
                        while ( my ( $itemtype, $description ) =
                            $sth->fetchrow_array )
                        {
                            push @authorised_values, $itemtype;
                            $authorised_lib{$itemtype} = $description;
                        }

                        #---- "true" authorised value
                    }
                    else {
                        $authorised_values_sth->execute(
                            $tagslib->{$tag}->{$subfield}->{authorised_value} );
                        push @authorised_values, ""
                          unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
                        while ( my ( $value, $lib ) =
                            $authorised_values_sth->fetchrow_array )
                        {
                            push @authorised_values, $value;
                            $authorised_lib{$value} = $lib;
                        }
                    }
                    $subfield_data{marc_value} = CGI::scrolling_list(
                        -name     => 'field_value',
                        -values   => \@authorised_values,
                        -default  => "$value",
                        -labels   => \%authorised_lib,
                        -size     => 1,
                        -tabindex => '',
                        -multiple => 0,
                    );
                }
                elsif ( $tagslib->{$tag}->{$subfield}->{thesaurus_category} ) {
                    $subfield_data{marc_value} =
"<input type=\"text\" name=\"field_value\"  size=47 maxlength=255> <a href=\"javascript:Dopop('cataloguing/thesaurus_popup.pl?category=$tagslib->{$tag}->{$subfield}->{thesaurus_category}&index=',)\">...</a>";

#"
# COMMENTED OUT because No $i is provided with this API.
# And thus, no value_builder can be activated.
# BUT could be thought over.
#         } elsif ($tagslib->{$tag}->{$subfield}->{'value_builder'}) {
#             my $plugin="value_builder/".$tagslib->{$tag}->{$subfield}->{'value_builder'};
#             require $plugin;
#             my $extended_param = plugin_parameters($dbh,$itemrecord,$tagslib,$i,0);
#             my ($function_name,$javascript) = plugin_javascript($dbh,$record,$tagslib,$i,0);
#             $subfield_data{marc_value}="<input type=\"text\" value=\"$value\" name=\"field_value\"  size=47 maxlength=255 DISABLE READONLY OnFocus=\"javascript:Focus$function_name()\" OnBlur=\"javascript:Blur$function_name()\"> <a href=\"javascript:Clic$function_name()\">...</a> $javascript";
                }
                else {
                    $subfield_data{marc_value} =
"<input type=\"text\" name=\"field_value\" value=\"$value\" size=50 maxlength=255>";
                }
                push( @loop_data, \%subfield_data );
            }
        }
    }
    my $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield )
      if ( $itemrecord && $itemrecord->field($itemtagfield) );
    return {
        'itemtagfield'    => $itemtagfield,
        'itemtagsubfield' => $itemtagsubfield,
        'itemnumber'      => $itemnumber,
        'iteminformation' => \@loop_data
    };
}

=head2 nsb_clean

my $string = nsb_clean( $string, $encoding );

=cut

sub nsb_clean {
    my $NSB      = '\x88';    # NSB : begin Non Sorting Block
    my $NSE      = '\x89';    # NSE : Non Sorting Block end
                              # handles non sorting blocks
    my ($string) = @_;
    $_ = $string;
    s/$NSB/(/gm;
    s/[ ]{0,1}$NSE/) /gm;
    $string = $_;
    return ($string);
}

=head2 zebraopfiles

&zebraopfiles( $dbh, $biblionumber, $record, $folder, $server );

=cut

sub zebraopfiles {

    my ( $dbh, $biblionumber, $record, $folder, $server ) = @_;

    my $op;
    my $zebradir =
      C4::Context->zebraconfig($server)->{directory} . "/" . $folder . "/";
    unless ( opendir( DIR, "$zebradir" ) ) {
        warn "$zebradir not found";
        return;
    }
    closedir DIR;
    my $filename = $zebradir . $biblionumber;

    if ($record) {
        open( OUTPUT, ">", $filename . ".xml" );
        print OUTPUT $record;
        close OUTPUT;
    }
}

=head2 zebraop

zebraop( $dbh, $biblionumber, $op, $server );

=cut

sub zebraop {
###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
    my ( $biblionumber, $op, $server ) = @_;
    my $dbh=C4::Context->dbh;
    #warn "SERVER:".$server;
#
# true zebraop 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 zebraop to run.
# the table is emptied by misc/cronjobs/zebraqueue_start.pl script

my $sth=$dbh->prepare("insert into zebraqueue  (biblio_auth_number ,server,operation) values(?,?,?)");
$sth->execute($biblionumber,$server,$op);
$sth->finish;

#
#     my @Zconnbiblio;
#     my $tried     = 0;
#     my $recon     = 0;
#     my $reconnect = 0;
#     my $record;
#     my $shadow;
# 
#   reconnect:
#     $Zconnbiblio[0] = C4::Context->Zconn( $server, 0, 1 );
# 
#     if ( $server eq "biblioserver" ) {
# 
#         # it's unclear to me whether this should be in xml or MARC format
#         # but it is clear it should be nabbed from zebra rather than from
#         # the koha tables
#         $record = GetMarcBiblio($biblionumber);
#         $record = $record->as_xml_record() if $record;
# #            warn "RECORD $biblionumber => ".$record;
#         $shadow="biblioservershadow";
# 
#         #           warn "RECORD $biblionumber => ".$record;
#         $shadow = "biblioservershadow";
# 
#     }
#     elsif ( $server eq "authorityserver" ) {
#         $record = C4::AuthoritiesMarc::XMLgetauthority( $dbh, $biblionumber );
#         $shadow = "authorityservershadow";
#     }    ## Add other servers as necessary
# 
#     my $Zpackage = $Zconnbiblio[0]->package();
#     $Zpackage->option( action => $op );
#     $Zpackage->option( record => $record );
# 
#   retry:
#     $Zpackage->send("update");
#     my $i;
#     my $event;
# 
#     while ( ( $i = ZOOM::event( \@Zconnbiblio ) ) != 0 ) {
#         $event = $Zconnbiblio[0]->last_event();
#         last if $event == ZOOM::Event::ZEND;
#     }
# 
#     my ( $error, $errmsg, $addinfo, $diagset ) = $Zconnbiblio[0]->error_x();
#     if ( $error == 10000 && $reconnect == 0 )
#     {    ## This is serious ZEBRA server is not available -reconnect
#         warn "problem with zebra server connection";
#         $reconnect = 1;
#         my $res = system('sc start "Z39.50 Server" >c:/zebraserver/error.log');
# 
#         #warn "Trying to restart ZEBRA Server";
#         #goto "reconnect";
#     }
#     elsif ( $error == 10007 && $tried < 2 )
#     {    ## timeout --another 30 looonng seconds for this update
#         $tried = $tried + 1;
#         warn "warn: timeout, trying again";
#         goto "retry";
#     }
#     elsif ( $error == 10004 && $recon == 0 ) {    ##Lost connection -reconnect
#         $recon = 1;
#         warn "error: reconnecting to zebra";
#         goto "reconnect";
# 
#    # as a last resort, we save the data to the filesystem to be indexed in batch
#     }
#     elsif ($error) {
#         warn
# "Error-$server   $op $biblionumber /errcode:, $error, /MSG:,$errmsg,$addinfo \n";
#         $Zpackage->destroy();
#         $Zconnbiblio[0]->destroy();
#         zebraopfiles( $dbh, $biblionumber, $record, $op, $server );
#         return;
#     }
#     if ( C4::Context->$shadow ) {
#         $Zpackage->send('commit');
#         while ( ( $i = ZOOM::event( \@Zconnbiblio ) ) != 0 ) {
# 
#             #waiting zebra to finish;
#          }
#     }
#     $Zpackage->destroy();
}

=head2 calculatelc

$lc = calculatelc($classification);

=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

$cutterextra = itemcalculator( $dbh, $biblioitem, $callnumber );

=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;
}

END { }    # module clean-up code here (global destructor)

1;

__END__

=head1 AUTHOR

Koha Developement team <info@koha.org>

Paul POULAIN paul.poulain@free.fr

Joshua Ferraro jmf@liblime.com

=cut

# $Id$
# $Log$
# 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.
#
# Revision 1.188  2007/03/09 14:31:47  tipaul
# rel_3_0 moved to HEAD
#
# Revision 1.178.2.59  2007/02/28 10:01:13  toins
# reporting bug fix from 2.2.7.1 to rel_3_0
# LOG was :
# 		BUGFIX/improvement : limiting MARCsubject to 610 as 676 is dewey, and is somewhere else
#
# Revision 1.178.2.58  2007/02/05 16:50:01  toins
# fix a mod_perl bug:
# There was a global var modified into an internal function in {MARC|ISBD}detail.pl.
# Moving this function in Biblio.pm
#
# Revision 1.178.2.57  2007/01/25 09:37:58  tipaul
# removing warn
#
# Revision 1.178.2.56  2007/01/24 13:50:26  tipaul
# Acquisition fix
# removing newbiblio & newbiblioitems subs.
# adding Koha2Marc
#
# IMHO, all biblio handling is better handled if they are done in a single place, the subs with MARC::Record as parameters.
# newbiblio & newbiblioitems where koha 1.x subs, that are called when MARC=OFF (which is not working anymore in koha 3.0, unless someone reintroduce it), and in acquisition module.
# The Koha2Marc sub moves a hash (with biblio/biblioitems subfield as keys) into a MARC::Record, that can be used to call NewBiblio, the standard biblio manager sub.
#
# Revision 1.178.2.55  2007/01/17 18:07:17  alaurin
# bugfixing for zebraqueue_start and biblio.pm :
#
# 	- Zebraqueue_start : restoring function of deletion in zebraqueue DB list
#
# 	-biblio.pm : changing method of default_record_format, now we have :
# 		MARC::File::XML->default_record_format(C4::Context->preference('marcflavour'));
#
# 	with this line the encoding in zebra seems to be ok (in unimarc and marc21)
#
# Revision 1.178.2.54  2007/01/16 15:00:03  tipaul
# donc try to delete the biblio in koha, just fill zebraqueue table !
#
# Revision 1.178.2.53  2007/01/16 10:24:11  tipaul
# BUGFIXING :
# when modifying or deleting an item, the biblio frameworkcode was emptied.
#
# Revision 1.178.2.52  2007/01/15 17:20:55  toins
# *** empty log message ***
#
# Revision 1.178.2.51  2007/01/15 15:16:44  hdl
# Uncommenting zebraop.
#
# Revision 1.178.2.50  2007/01/15 14:59:09  hdl
# Adding creation of an unexpected serial any time.
# +
# USING Date::Calc and not Date::Manip.
# WARNING : There are still some Bugs in next issue date management. (Date::Calc donot wrap easily next year calculation.)
#
# Revision 1.178.2.49  2007/01/12 10:12:30  toins
# writing $record->as_formatted in the log when Modifying an item.
#
# Revision 1.178.2.48  2007/01/11 16:33:04  toins
# write $record->as_formatted into the log.
#
# Revision 1.178.2.47  2007/01/10 16:46:27  toins
# Theses modules need to use C4::Log.
#
# Revision 1.178.2.46  2007/01/10 16:31:15  toins
# new systems preferences :
#  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
#  - BorrowersLog ( idem for borrowers )
#  - IssueLog (log all issue if set to 1)
#  - ReturnLog (log all return if set to 1)
#  - SusbcriptionLog (log all creation/deletion/update of a subcription)
#
# All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
#
# Revision 1.178.2.45  2007/01/09 10:31:09  toins
# sync with dev_week. ( new function : GetMarcSeries )
#
# Revision 1.178.2.44  2007/01/04 17:41:32  tipaul
# 2 major bugfixes :
# - deletion of an item deleted the whole biblio because of a wrong API
# - create an item was bugguy for default framework
#
# Revision 1.178.2.43  2006/12/22 15:09:53  toins
# removing C4::Database;
#
# Revision 1.178.2.42  2006/12/20 16:51:00  tipaul
# ZEBRA update :
# - adding a new table : when a biblio is added/modified/ deleted, an entry is entered in this table
# - the zebraqueue_start.pl script read it & does the stuff.
#
# code coming from head (tumer). it can be run every minut instead of once every day for dev_week code.
#
# I just have commented the previous code (=real time update) in Biblio.pm, we will be able to reactivate it once indexdata fixes zebra update bug !
#
# Revision 1.178.2.41  2006/12/20 08:54:44  toins
# GetXmlBiblio wasn't exported.
#
# Revision 1.178.2.40  2006/12/19 16:45:56  alaurin
# bugfixing, for zebra and authorities
#
# Revision 1.178.2.39  2006/12/08 17:55:44  toins
# GetMarcAuthors now get authors for all subfields
#
# Revision 1.178.2.38  2006/12/07 15:42:14  toins
# synching opac & intranet.
# fix some broken link & bugs.
# removing warn compilation.
#
# Revision 1.178.2.37  2006/12/07 11:09:39  tipaul
# MAJOR FIX :
# the ->destroy() line destroys the zebra connection. When we are running koha as cgi, it's not a problem, as the script dies after each request.
# BUT for bulkmarcimport & mod_perl, the zebra conn must be persistant.
#
# Revision 1.178.2.36  2006/12/06 16:54:21  alaurin
# restore function zebraop for delete biblios :
#
# 1) restore C4::Circulation::Circ2::itemissues, (was missing)
# 2) restore zebraop value : delete_record
#
# Revision 1.178.2.35  2006/12/06 10:02:12  alaurin
# bugfixing for delete a biblio :
#
# restore itemissue fonction .... :
#
# other is pointed, zebra error 224... for biblio is not deleted in zebra ..
# ....
#
# Revision 1.178.2.34  2006/12/06 09:14:25  toins
# Correct the link to the MARC subjects.
#
# Revision 1.178.2.33  2006/12/05 11:35:29  toins
# Biblio.pm cleaned.
# additionalauthors, bibliosubject, bibliosubtitle tables are now unused.
# Some functions renamed according to the coding guidelines.
#
# Revision 1.178.2.32  2006/12/04 17:39:57  alaurin
# bugfix :
#
# restore zebraop for update zebra
#
# Revision 1.178.2.31  2006/12/01 17:00:19  tipaul
# additem needs $frameworkcode
#
# Revision 1.178.2.30  2006/11/30 18:23:51  toins
# theses scripts don't need to use C4::Search.
#
# Revision 1.178.2.29  2006/11/30 17:17:01  toins
# following functions moved from Search.p to Biblio.pm :
# - bibdata
# - itemsissues
# - addauthor
# - getMARCNotes
# - getMARCsubjects
#
# Revision 1.178.2.28  2006/11/28 15:15:03  toins
# sync with dev_week.
# (deleteditems table wasn't getting populaated because the execute was commented out. This puts it back
#     -- some table changes are needed as well, I'll commit those separately.)
#
# Revision 1.178.2.27  2006/11/20 16:52:05  alaurin
# minor bugfixing :
#
# correcting in _koha_modify_biblioitem : restore the biblionumber line .
#
# now the sql update of biblioitems is ok ....
#
# Revision 1.178.2.26  2006/11/17 14:57:21  tipaul
# code cleaning : moving bornum, borrnum, bornumber to a correct "borrowernumber"
#
# Revision 1.178.2.25  2006/11/17 13:18:58  tipaul
# code cleaning : removing use of "bib", and replacing with "biblionumber"
#
# WARNING : I tried to do carefully, but there are probably some mistakes.
# So if you encounter a problem you didn't have before, look for this change !!!
# anyway, I urge everybody to use only "biblionumber", instead of "bib", "bi", "biblio" or anything else. will be easier to maintain !!!
#
# Revision 1.178.2.24  2006/11/17 11:18:47  tipaul
# * removing useless subs
# * moving bibid to biblionumber where needed
#
# Revision 1.178.2.23  2006/11/17 09:39:04  btoumi
# bug fix double declaration of variable in same function
#
# Revision 1.178.2.22  2006/11/15 15:15:50  hdl
# Final First Version for New Facility for subscription management.
#
# Now
# use serials-collection.pl for history display
# and serials-edit.pl for serial edition
# subscription add and detail adds a new branch information to help IndependantBranches Library to manage different subscriptions for a serial
#
# This is aimed at replacing serials-receive and statecollection.
#
# Revision 1.178.2.21  2006/11/15 14:49:38  tipaul
# in some cases, there are invalid utf8 chars in XML (at least in SANOP). this commit remove them on the fly.
# Not sure it's a good idea to keep them in biblio.pm, let me know your opinion on koha-devel if you think it's a bad idea...
#
# Revision 1.178.2.20  2006/10/31 17:20:49  toins
# * moving bibitemdata from search to here.
# * using _koha_modify_biblio instead of OLDmodbiblio.
#
# Revision 1.178.2.19  2006/10/20 15:26:41  toins
# sync with dev_week.
#
# Revision 1.178.2.18  2006/10/19 11:57:04  btoumi
# bug fix : wrong syntax in sub call
#
# Revision 1.178.2.17  2006/10/17 09:54:42  toins
# ccode (re)-integration.
#
# Revision 1.178.2.16  2006/10/16 16:20:34  toins
# MARCgetbiblio cleaned up.
#
# Revision 1.178.2.15  2006/10/11 14:26:56  tipaul
# handling of UNIMARC :
# - better management of field 100 = automatic creation of the field if needed & filling encoding to unicode.
# - better management of encoding (MARC::File::XML new_from_xml()). This fix works only on my own version of M:F:XML, i think the actual one is buggy & have reported the problem to perl4lib mailing list
# - fixing a bug on MARCgetitem, that uses biblioitems.marc and not biblioitems.marcxml
#
# Revision 1.178.2.14  2006/10/11 07:59:36  tipaul
# removing hardcoded ccode fiels in biblioitems
#
# Revision 1.178.2.13  2006/10/10 14:21:24  toins
# Biblio.pm now returns a true value.
#
# Revision 1.178.2.12  2006/10/09 16:44:23  toins
# Sync with dev_week.
#
# Revision 1.178.2.11  2006/10/06 13:23:49  toins
# Synch with dev_week.
#
# Revision 1.178.2.10  2006/10/02 09:32:02  hdl
# Adding GetItemStatus and GetItemLocation function in order to make serials-receive.pl work.
#
# *************WARNING.***************
# tested for UNIMARC and using 'marcflavour' system preferences to set defaut_record_format.
#
# Revision 1.178.2.9  2006/09/26 07:54:20  hdl
# Bug FIX: Correct accents for UNIMARC biblio MARC details.
# (Adding the use of default_record_format in MARCgetbiblio if UNIMARC marcflavour is chosen. This should be widely used as soon as we use xml records)
#
# Revision 1.178.2.8  2006/09/25 14:46:22  hdl
# Now using iso2709 MARC data for MARC.
# (Works better for accents than XML)
#
# Revision 1.178.2.7  2006/09/20 13:44:14  hdl
# Bug Fixing : Cataloguing was broken for UNIMARC.
# Please test.

