# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
-use strict;
-use warnings;
+use Modern::Perl;
use Carp;
use Encode qw( decode is_utf8 );
use C4::OAI::Sets;
use C4::Debug;
-use Koha::Cache;
+use Koha::Caches;
use Koha::Authority::Types;
use Koha::Acquisition::Currencies;
+use Koha::Biblio::Metadata;
+use Koha::Biblio::Metadatas;
+use Koha::Holds;
+use Koha::ItemTypes;
use Koha::SearchEngine;
+use Koha::Libraries;
use vars qw(@ISA @EXPORT);
use vars qw($debug $cgi_debug);
# to get something
push @EXPORT, qw(
- GetBiblio
GetBiblioData
GetMarcBiblio
GetBiblioItemData
GetBiblioItemInfosOf
GetBiblioItemByBiblioNumber
- GetBiblioFromItemNumber
- GetBiblionumberFromItemnumber
&GetRecordValue
- &GetFieldMapping
- &SetFieldMapping
- &DeleteFieldMapping
&GetISBDView
&CountItemsIssued
&CountBiblioInOrders
- &GetSubscriptionsId
- &GetHolds
);
# To modify something
push @EXPORT, qw(
&ModBiblio
- &ModBiblioframework
&ModZebra
&UpdateTotalIssues
&RemoveAllNsb
=item 2. as raw MARC in the Zebra index and storage engine
-=item 3. as raw MARC the biblioitems.marc and biblioitems.marcxml
+=item 3. as MARC XML in biblio_metadata.metadata
=back
-In the 3.0 version of Koha, the authoritative record-level information is in biblioitems.marcxml
+In the 3.0 version of Koha, the authoritative record-level information is in biblio_metadata.metadata
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. However, if this occur, it can be considered as a bug : The API is (or should be) complete & the only entry point for all biblio/items managements.
=item 2. _koha_* - low-level internal functions for managing the koha tables
-=item 3. Marc management function : as the MARC record is stored in biblioitems.marc(xml), some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
+=item 3. Marc management function : as the MARC record is stored in biblio_metadata.metadata, some subs dedicated to it's management are in this package. They should be used only internally by Biblio.pm, the only official entry points being AddBiblio, AddItem, ModBiblio, ModItem.
=item 4. Zebra functions used to update the Zebra index
=back
-The MARC record (in biblioitems.marcxml) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
+The MARC record (in biblio_metadata.metadata) contains the complete marc record, including items. It also contains the biblionumber. That is the reason why it is not stored directly by AddBiblio, with all other fields . To save a biblio, we need to :
=over 4
=back
-When dealing with items, we must :
-
-=over 4
-
-=item 1. save the item in items table, that gives us an itemnumber
-
-=item 2. add the itemnumber to the item MARC field
-
-=item 3. overwrite the MARC record (with the added item) into biblioitems.marc(xml)
-
-When modifying a biblio or an item, the behaviour is quite similar.
-
-=back
-
=head1 EXPORTED FUNCTIONS
=head2 AddBiblio
This function also accepts a third, optional argument: a hashref
to additional options. The only defined option is C<defer_marc_save>,
which if present and mapped to a true value, causes C<AddBiblio>
-to omit the call to save the MARC in C<bibilioitems.marc>
-and C<biblioitems.marcxml> This option is provided B<only>
+to omit the call to save the MARC in C<biblio_metadata.metadata>
+This option is provided B<only>
for the use of scripts such as C<bulkmarcimport.pl> that may need
to do some manipulation of the MARC record for item parsing before
saving it and which cannot afford the performance hit of saving
}
}
-=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=?" );
- $sth->execute( $frameworkcode, $biblionumber );
- return 1;
-}
-
=head2 DelBiblio
my $error = &DelBiblio($biblionumber);
}
# We delete any existing holds
+ my $biblio = Koha::Biblios->find( $biblionumber );
+ my $holds = $biblio->holds;
require C4::Reserves;
- my $reserves = C4::Reserves::GetReservesFromBiblionumber({ biblionumber => $biblionumber });
- foreach my $res ( @$reserves ) {
- C4::Reserves::CancelReserve({ reserve_id => $res->{'reserve_id'} });
+ while ( my $hold = $holds->next ) {
+ C4::Reserves::CancelReserve({ reserve_id => $hold->reserve_id }); # TODO Replace with $hold->cancel
}
# Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
return $error if $error;
}
+
# delete biblio from Koha tables and save in deletedbiblio
# must do this *after* _koha_delete_biblioitems, otherwise
# delete cascade will prevent deletedbiblioitems rows
return \@result;
}
-=head2 SetFieldMapping
-
- SetFieldMapping($framework, $field, $fieldcode, $subfieldcode);
-
-Set a Field to MARC mapping value, if it already exists we don't add a new one.
-
-=cut
-
-sub SetFieldMapping {
- my ( $framework, $field, $fieldcode, $subfieldcode ) = @_;
- my $dbh = C4::Context->dbh;
-
- my $sth = $dbh->prepare('SELECT * FROM fieldmapping WHERE fieldcode = ? AND subfieldcode = ? AND frameworkcode = ? AND field = ?');
- $sth->execute( $fieldcode, $subfieldcode, $framework, $field );
- if ( not $sth->fetchrow_hashref ) {
- my @args;
- $sth = $dbh->prepare('INSERT INTO fieldmapping (fieldcode, subfieldcode, frameworkcode, field) VALUES(?,?,?,?)');
-
- $sth->execute( $fieldcode, $subfieldcode, $framework, $field );
- }
-}
-
-=head2 DeleteFieldMapping
-
- DeleteFieldMapping($id);
-
-Delete a field mapping from an $id.
-
-=cut
-
-sub DeleteFieldMapping {
- my ($id) = @_;
- my $dbh = C4::Context->dbh;
-
- my $sth = $dbh->prepare('DELETE FROM fieldmapping WHERE id = ?');
- $sth->execute($id);
-}
-
-=head2 GetFieldMapping
-
- GetFieldMapping($frameworkcode);
-
-Get all field mappings for a specified frameworkcode
-
-=cut
-
-sub GetFieldMapping {
- my ($framework) = @_;
- my $dbh = C4::Context->dbh;
-
- my $sth = $dbh->prepare('SELECT * FROM fieldmapping where frameworkcode = ?');
- $sth->execute($framework);
-
- my @return;
- while ( my $row = $sth->fetchrow_hashref ) {
- push @return, $row;
- }
- return \@return;
-}
-
=head2 GetBiblioData
$data = &GetBiblioData($biblionumber);
return @results;
}
-=head2 GetBiblionumberFromItemnumber
-
-
-=cut
-
-sub GetBiblionumberFromItemnumber {
- my ($itemnumber) = @_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("Select biblionumber FROM items WHERE itemnumber = ?");
-
- $sth->execute($itemnumber);
- my ($result) = $sth->fetchrow;
- return ($result);
-}
-
-=head2 GetBiblioFromItemNumber
-
- $item = &GetBiblioFromItemNumber($itemnumber,$barcode);
-
-Looks up the item with the given itemnumber. if undef, try the barcode.
-
-C<&itemnodata> returns a reference-to-hash whose keys are the fields
-from the C<biblio>, C<biblioitems>, and C<items> tables in the Koha
-database.
-
-=cut
-
-#'
-sub GetBiblioFromItemNumber {
- my ( $itemnumber, $barcode ) = @_;
- my $dbh = C4::Context->dbh;
- my $sth;
- if ($itemnumber) {
- $sth = $dbh->prepare(
- "SELECT * FROM items
- LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
- LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
- WHERE items.itemnumber = ?"
- );
- $sth->execute($itemnumber);
- } else {
- $sth = $dbh->prepare(
- "SELECT * FROM items
- LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
- LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
- WHERE items.barcode = ?"
- );
- $sth->execute($barcode);
- }
- my $data = $sth->fetchrow_hashref;
- $sth->finish;
- return ($data);
-}
-
=head2 GetISBDView
- $isbd = &GetISBDView($biblionumber);
+ $isbd = &GetISBDView({
+ 'record' => $marc_record,
+ 'template' => $interface, # opac/intranet
+ 'framework' => $framework,
+ });
Return the ISBD view which can be included in opac and intranet
=cut
sub GetISBDView {
- my ( $biblionumber, $template ) = @_;
- my $record = GetMarcBiblio($biblionumber, 1);
- $template ||= '';
- my $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd';
+ my ( $params ) = @_;
+
+ # Expecting record WITH items.
+ my $record = $params->{record};
return unless defined $record;
- my $itemtype = &GetFrameworkCode($biblionumber);
+
+ my $template = $params->{template} // q{};
+ my $sysprefname = $template eq 'opac' ? 'opacisbd' : 'isbd';
+ my $framework = $params->{framework};
+ my $itemtype = $framework;
my ( $holdingbrtagf, $holdingbrtagsubf ) = &GetMarcFromKohaField( "items.holdingbranch", $itemtype );
- my $tagslib = &GetMarcStructure( 1, $itemtype );
+ my $tagslib = &GetMarcStructure( 1, $itemtype, { unsafe => 1 } );
my $ISBD = C4::Context->preference($sysprefname);
my $bloc = $ISBD;
return $res;
}
-=head2 GetBiblio
-
- my $biblio = &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);
- if ( my $data = $sth->fetchrow_hashref ) {
- return $data;
- }
- return;
-} # sub GetBiblio
-
=head2 GetBiblioItemInfosOf
GetBiblioItemInfosOf(@biblioitemnumbers);
my $biblioitemnumber_values = @biblioitemnumbers ? join( ',', @biblioitemnumbers ) : "''";
+ my $dbh = C4::Context->dbh;
my $query = "
SELECT biblioitemnumber,
publicationyear,
FROM biblioitems
WHERE biblioitemnumber IN ($biblioitemnumber_values)
";
- return get_infos_of( $query, 'biblioitemnumber' );
+ return $dbh->selectall_hashref($query, 'biblioitemnumber');
}
=head1 FUNCTIONS FOR HANDLING MARC MANAGEMENT
=head2 GetMarcStructure
- $res = GetMarcStructure($forlibrarian,$frameworkcode);
+ $res = GetMarcStructure($forlibrarian, $frameworkcode, [ $params ]);
Returns a reference to a big hash of hash, with the Marc structure for the given frameworkcode
$forlibrarian :if set to 1, the MARC descriptions are the librarians ones, otherwise it's the public (OPAC) ones
$frameworkcode : the framework code to read
+$params allows you to pass { unsafe => 1 } for better performance.
+
+Note: If you call GetMarcStructure with unsafe => 1, do not modify or
+even autovivify its contents. It is a cached/shared data structure. Your
+changes c/would be passed around in subsequent calls.
=cut
sub GetMarcStructure {
- my ( $forlibrarian, $frameworkcode ) = @_;
+ my ( $forlibrarian, $frameworkcode, $params ) = @_;
$frameworkcode = "" unless $frameworkcode;
$forlibrarian = $forlibrarian ? 1 : 0;
- my $cache = Koha::Cache->get_instance();
+ my $unsafe = ($params && $params->{unsafe})? 1: 0;
+ my $cache = Koha::Caches->get_instance();
my $cache_key = "MarcStructure-$forlibrarian-$frameworkcode";
- my $cached = $cache->get_from_cache($cache_key);
+ my $cached = $cache->get_from_cache($cache_key, { unsafe => $unsafe });
return $cached if $cached;
my $dbh = C4::Context->dbh;
sub GetUsedMarcStructure {
my $frameworkcode = shift || '';
- my $query = qq/
+ my $query = q{
SELECT *
FROM marc_subfield_structure
WHERE tab > -1
AND frameworkcode = ?
ORDER BY tagfield, tagsubfield
- /;
+ };
my $sth = C4::Context->dbh->prepare($query);
$sth->execute($frameworkcode);
return $sth->fetchall_arrayref( {} );
}
+=head2 GetMarcSubfieldStructure
+
+=cut
+
+sub GetMarcSubfieldStructure {
+ my ( $frameworkcode ) = @_;
+
+ $frameworkcode //= '';
+
+ my $cache = Koha::Caches->get_instance();
+ my $cache_key = "MarcSubfieldStructure-$frameworkcode";
+ my $cached = $cache->get_from_cache($cache_key);
+ return $cached if $cached;
+
+ my $dbh = C4::Context->dbh;
+ my $subfield_structure = $dbh->selectall_hashref( q|
+ SELECT *
+ FROM marc_subfield_structure
+ WHERE frameworkcode = ?
+ AND kohafield > ''
+ |, 'kohafield', {}, $frameworkcode );
+
+ $cache->set_in_cache( $cache_key, $subfield_structure );
+ return $subfield_structure;
+}
+
=head2 GetMarcFromKohaField
($MARCfield,$MARCsubfield)=GetMarcFromKohaField($kohafield,$frameworkcode);
=cut
sub GetMarcFromKohaField {
- my $kohafield = shift;
- my $frameworkcode = shift || '';
+ my ( $kohafield, $frameworkcode ) = @_;
return (0, undef) unless $kohafield;
- my $relations = C4::Context->marcfromkohafield;
- if ( my $mf = $relations->{$frameworkcode}->{$kohafield} ) {
- return @$mf;
- }
- return (0, undef);
+ my $mss = GetMarcSubfieldStructure( $frameworkcode );
+ return ( $mss->{$kohafield}{tagfield}, $mss->{$kohafield}{tagsubfield} );
}
=head2 GetMarcSubfieldStructureFromKohaField
=cut
sub GetMarcSubfieldStructureFromKohaField {
- my ($kohafield, $frameworkcode) = @_;
-
- return undef unless $kohafield;
- $frameworkcode //= '';
+ my ( $kohafield, $frameworkcode ) = @_;
- my $dbh = C4::Context->dbh;
- my $query = qq{
- SELECT *
- FROM marc_subfield_structure
- WHERE kohafield = ?
- AND frameworkcode = ?
- };
- my $sth = $dbh->prepare($query);
- $sth->execute($kohafield, $frameworkcode);
- my $result = $sth->fetchrow_hashref;
- $sth->finish;
+ return unless $kohafield;
- return $result;
+ my $mss = GetMarcSubfieldStructure( $frameworkcode );
+ return exists $mss->{$kohafield}
+ ? $mss->{$kohafield}
+ : undef;
}
=head2 GetMarcBiblio
}
my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("SELECT biblioitemnumber, marcxml FROM biblioitems WHERE biblionumber=? ");
+ my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=? ");
$sth->execute($biblionumber);
my $row = $sth->fetchrow_hashref;
my $biblioitemnumber = $row->{'biblioitemnumber'};
- my $marcxml = StripNonXmlChars( $row->{'marcxml'} );
+ my $marcxml = GetXmlBiblio( $biblionumber );
+ $marcxml = StripNonXmlChars( $marcxml );
my $frameworkcode = GetFrameworkCode($biblionumber);
MARC::File::XML->default_record_format( C4::Context->preference('marcflavour') );
my $record = MARC::Record->new();
my $marcxml = GetXmlBiblio($biblionumber);
-Returns biblioitems.marcxml of the biblionumber passed in parameter.
+Returns biblio_metadata.metadata/marcxml of the biblionumber passed in parameter.
The XML should only contain biblio information (item information is no longer stored in marcxml field)
=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;
+ my $dbh = C4::Context->dbh;
+ return unless $biblionumber;
+ my ($marcxml) = $dbh->selectrow_array(
+ q|
+ SELECT metadata
+ FROM biblio_metadata
+ WHERE biblionumber=?
+ AND format='marcxml'
+ AND marcflavour=?
+ |, undef, $biblionumber, C4::Context->preference('marcflavour')
+ );
return $marcxml;
}
=head2 MungeMarcPrice
Return the best guess at what the actual price is from a price field.
+
=cut
sub MungeMarcPrice {
#---- branch
if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
- return C4::Branch::GetBranchName($value);
+ return Koha::Libraries->find($value)->branchname;
}
#---- itemtypes
if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "itemtypes" ) {
- return getitemtypeinfo($value)->{translated_description};
+ my $itemtype = Koha::ItemTypes->find( $value );
+ return $itemtype ? $itemtype->translated_description : q||;
}
#---- "true" authorized value
push @marcsubjects, {
MARCSUBJECT_SUBFIELDS_LOOP => \@subfields_loop,
authoritylink => $authoritylink,
- };
+ } if $authoritylink || @subfields_loop;
}
return \@marcsubjects;
}
my ( $mintag, $maxtag, $fields_filter );
- # tagslib useful for UNIMARC author reponsabilities
- my $tagslib =
- &GetMarcStructure( 1, '' ); # FIXME : we don't have the framework available, we take the default framework. May be buggy on some setups, will be usually correct.
+ # tagslib useful only for UNIMARC author responsibilities
+ my $tagslib;
if ( $marcflavour eq "UNIMARC" ) {
+ # FIXME : we don't have the framework available, we take the default framework. May be buggy on some setups, will be usually correct.
+ $tagslib = GetMarcStructure( 1, '', { unsafe => 1 });
$mintag = "700";
$maxtag = "712";
$fields_filter = '7..';
}
my @urls = $field->subfield('u');
foreach my $url (@urls) {
+ $url =~ s/^\s+|\s+$//g; # trim
my $marcurl;
if ( $marcflavour eq 'MARC21' ) {
my $s3 = $field->subfield('3');
return $marchostsarray;
}
+=head2 UpsertMarcSubfield
+
+ my $record = C4::Biblio::UpsertMarcSubfield($MARC::Record, $fieldTag, $subfieldCode, $subfieldContent);
+
+=cut
+
+sub UpsertMarcSubfield {
+ my ($record, $tag, $code, $content) = @_;
+ my $f = $record->field($tag);
+
+ if ($f) {
+ $f->update( $code => $content );
+ }
+ else {
+ my $f = MARC::Field->new( $tag, '', '', $code => $content);
+ $record->insert_fields_ordered( $f );
+ }
+}
+
+=head2 UpsertMarcControlField
+
+ my $record = C4::Biblio::UpsertMarcControlField($MARC::Record, $fieldTag, $content);
+
+=cut
+
+sub UpsertMarcControlField {
+ my ($record, $tag, $content) = @_;
+ die "UpsertMarcControlField() \$tag '$tag' is not a control field\n" unless 0+$tag < 10;
+ my $f = $record->field($tag);
+
+ if ($f) {
+ $f->update( $content );
+ }
+ else {
+ my $f = MARC::Field->new($tag, $content);
+ $record->insert_fields_ordered( $f );
+ }
+}
+
=head2 GetFrameworkCode
$frameworkcode = GetFrameworkCode( $biblionumber )
my $hash = shift;
my $record = MARC::Record->new();
SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
- my $db_to_marc = C4::Context->marcfromkohafield;
+ # FIXME Do not we want to get the marc subfield structure for the biblio framework?
+ my $mss = GetMarcSubfieldStructure();
my $tag_hr = {};
- while ( my ($name, $value) = each %$hash ) {
- next unless my $dtm = $db_to_marc->{''}->{$name};
- next unless ( scalar( @$dtm ) );
- my ($tag, $letter) = @$dtm;
- $tag .= '';
+ while ( my ($kohafield, $value) = each %$hash ) {
+ next unless exists $mss->{$kohafield};
+ next unless $mss->{$kohafield};
+ my $tagfield = $mss->{$kohafield}{tagfield} . '';
+ my $tagsubfield = $mss->{$kohafield}{tagsubfield};
foreach my $value ( split(/\s?\|\s?/, $value, -1) ) {
next if $value eq '';
- $tag_hr->{$tag} //= [];
- push @{$tag_hr->{$tag}}, [($letter, $value)];
+ $tag_hr->{$tagfield} //= [];
+ push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
}
}
foreach my $tag (sort keys %$tag_hr) {
# if we are on biblionumber, store it in the MARC::Record (it may not be in the edited fields)
if ( $param eq 'biblionumber' ) {
if ( $biblionumbertagfield < 10 ) {
- $newfield = MARC::Field->new( $biblionumbertagfield, $cgi->param($param), );
+ $newfield = MARC::Field->new( $biblionumbertagfield, scalar $cgi->param($param), );
} else {
- $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => $cgi->param($param), );
+ $newfield = MARC::Field->new( $biblionumbertagfield, '', '', "$biblionumbertagsubfield" => scalar $cgi->param($param), );
}
push @fields, $newfield if ($newfield);
} elsif ( $param =~ /^tag_(\d*)_indicator1_/ ) { # new field start when having 'input name="..._indicator1_..."
if ( $tag < 10 ) { # no code for theses fields
# in MARC editor, 000 contains the leader.
next if $tag == $biblionumbertagfield;
+ my $fval= $cgi->param($params[$j+1]);
if ( $tag eq '000' ) {
# Force a fake leader even if not provided to avoid crashing
# during decoding MARC record containing UTF-8 characters
$record->leader(
- length( $cgi->param($params[$j+1]) ) == 24
- ? $cgi->param( $params[ $j + 1 ] )
+ length( $fval ) == 24
+ ? $fval
: ' nam a22 4500'
)
;
# between 001 and 009 (included)
- } elsif ( $cgi->param( $params[ $j + 1 ] ) ne '' ) {
- $newfield = MARC::Field->new( $tag, $cgi->param( $params[ $j + 1 ] ), );
+ } elsif ( $fval ne '' ) {
+ $newfield = MARC::Field->new( $tag, $fval, );
}
# > 009, deal with subfields
#if next param ne subfield, then it was probably empty
#try next param by incrementing j
if($params[$j+1]!~/_subfield_/) {$j++; next; }
+ my $fkey= $cgi->param($params[$j]);
my $fval= $cgi->param($params[$j+1]);
#check if subfield value not empty and field exists
if($fval ne '' && $newfield) {
- $newfield->add_subfields( $cgi->param($params[$j]) => $fval);
+ $newfield->add_subfields( $fkey => $fval);
}
elsif($fval ne '') {
- $newfield = MARC::Field->new( $tag, $ind1, $ind2, $cgi->param($params[$j]) => $fval );
+ $newfield = MARC::Field->new( $tag, $ind1, $ind2, $fkey => $fval );
}
$j += 2;
} #end-of-while
return $record;
}
-# cache inverted MARC field map
-our $inverted_field_map;
-
=head2 TransformMarcToKoha
$result = TransformMarcToKoha( $record, $frameworkcode )
$limit_table = $limit_table || 0;
$frameworkcode = '' unless defined $frameworkcode;
- unless ( defined $inverted_field_map ) {
- $inverted_field_map = _get_inverted_marc_field_map();
- }
+ my $inverted_field_map = _get_inverted_marc_field_map($frameworkcode);
my %tables = ();
if ( defined $limit_table && $limit_table eq 'items' ) {
# traverse through record
MARCFIELD: foreach my $field ( $record->fields() ) {
my $tag = $field->tag();
- next MARCFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag};
+ next MARCFIELD unless exists $inverted_field_map->{$tag};
if ( $field->is_control_field() ) {
- my $kohafields = $inverted_field_map->{$frameworkcode}->{$tag}->{list};
+ my $kohafields = $inverted_field_map->{$tag}->{list};
ENTRY: foreach my $entry ( @{$kohafields} ) {
my ( $subfield, $table, $column ) = @{$entry};
next ENTRY unless exists $tables{$table};
# deal with subfields
MARCSUBFIELD: foreach my $sf ( $field->subfields() ) {
my $code = $sf->[0];
- next MARCSUBFIELD unless exists $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code};
+ next MARCSUBFIELD unless exists $inverted_field_map->{$tag}->{sfs}->{$code};
my $value = $sf->[1];
- SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$frameworkcode}->{$tag}->{sfs}->{$code} } ) {
+ SFENTRY: foreach my $entry ( @{ $inverted_field_map->{$tag}->{sfs}->{$code} } ) {
my ( $table, $column ) = @{$entry};
next SFENTRY unless exists $tables{$table};
my $key = _disambiguate( $table, $column );
}
sub _get_inverted_marc_field_map {
+ my ( $frameworkcode ) = @_;
my $field_map = {};
- my $relations = C4::Context->marcfromkohafield;
+ my $mss = GetMarcSubfieldStructure( $frameworkcode );
- foreach my $frameworkcode ( keys %{$relations} ) {
- foreach my $kohafield ( keys %{ $relations->{$frameworkcode} } ) {
- next unless @{ $relations->{$frameworkcode}->{$kohafield} }; # not all columns are mapped to MARC tag & subfield
- my $tag = $relations->{$frameworkcode}->{$kohafield}->[0];
- my $subfield = $relations->{$frameworkcode}->{$kohafield}->[1];
- my ( $table, $column ) = split /[.]/, $kohafield, 2;
- push @{ $field_map->{$frameworkcode}->{$tag}->{list} }, [ $subfield, $table, $column ];
- push @{ $field_map->{$frameworkcode}->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
- }
+ foreach my $kohafield ( keys %{ $mss } ) {
+ next unless exists $mss->{$kohafield}; # not all columns are mapped to MARC tag & subfield
+ my $tag = $mss->{$kohafield}{tagfield};
+ my $subfield = $mss->{$kohafield}{tagsubfield};
+ my ( $table, $column ) = split /[.]/, $kohafield, 2;
+ push @{ $field_map->{$tag}->{list} }, [ $subfield, $table, $column ];
+ push @{ $field_map->{$tag}->{sfs}->{$subfield} }, [ $table, $column ];
}
return $field_map;
}
if ( C4::Context->preference('SearchEngine') eq 'Elasticsearch' ) {
# TODO abstract to a standard API that'll work for whatever
- require Koha::ElasticSearch::Indexer;
- my $indexer = Koha::ElasticSearch::Indexer->new(
+ require Koha::SearchEngine::Elasticsearch::Indexer;
+ my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new(
{
index => $server eq 'biblioserver'
? $Koha::SearchEngine::BIBLIOS_INDEX
sub _koha_marc_update_bib_ids {
my ( $record, $frameworkcode, $biblionumber, $biblioitemnumber ) = @_;
- # we must add bibnum and bibitemnum in MARC::Record...
- # we build the new field with biblionumber and biblioitemnumber
- # we drop the original field
- # we add the new builded field.
my ( $biblio_tag, $biblio_subfield ) = GetMarcFromKohaField( "biblio.biblionumber", $frameworkcode );
die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblio_tag;
my ( $biblioitem_tag, $biblioitem_subfield ) = GetMarcFromKohaField( "biblioitems.biblioitemnumber", $frameworkcode );
die qq{No biblioitemnumber tag for framework "$frameworkcode"} unless $biblioitem_tag;
- if ( $biblio_tag == $biblioitem_tag ) {
-
- # biblionumber & biblioitemnumber are in the same field (can't be <10 as fields <10 have only 1 value)
- my $new_field = MARC::Field->new(
- $biblio_tag, '', '',
- "$biblio_subfield" => $biblionumber,
- "$biblioitem_subfield" => $biblioitemnumber
- );
-
- # drop old field and create new one...
- my $old_field = $record->field($biblio_tag);
- $record->delete_field($old_field) if $old_field;
- $record->insert_fields_ordered($new_field);
+ if ( $biblio_tag < 10 ) {
+ C4::Biblio::UpsertMarcControlField( $record, $biblio_tag, $biblionumber );
} else {
-
- # biblionumber & biblioitemnumber are in different fields
-
- # deal with biblionumber
- my ( $new_field, $old_field );
- if ( $biblio_tag < 10 ) {
- $new_field = MARC::Field->new( $biblio_tag, $biblionumber );
- } else {
- $new_field = MARC::Field->new( $biblio_tag, '', '', "$biblio_subfield" => $biblionumber );
- }
-
- # drop old field and create new one...
- $old_field = $record->field($biblio_tag);
- $record->delete_field($old_field) if $old_field;
- $record->insert_fields_ordered($new_field);
-
- # deal with biblioitemnumber
- if ( $biblioitem_tag < 10 ) {
- $new_field = MARC::Field->new( $biblioitem_tag, $biblioitemnumber, );
- } else {
- $new_field = MARC::Field->new( $biblioitem_tag, '', '', "$biblioitem_subfield" => $biblioitemnumber, );
- }
-
- # drop old field and create new one...
- $old_field = $record->field($biblioitem_tag);
- $record->delete_field($old_field) if $old_field;
- $record->insert_fields_ordered($new_field);
+ C4::Biblio::UpsertMarcSubfield($record, $biblio_tag, $biblio_subfield, $biblionumber);
+ }
+ if ( $biblioitem_tag < 10 ) {
+ C4::Biblio::UpsertMarcControlField( $record, $biblioitem_tag, $biblioitemnumber );
+ } else {
+ C4::Biblio::UpsertMarcSubfield($record, $biblioitem_tag, $biblioitem_subfield, $biblioitemnumber);
}
}
my ($biblioitemnumber,$error) = _koha_modify_biblioitem_nonmarc( $dbh, $biblioitem );
-Updates biblioitems row except for marc and marcxml, which should be changed
-via ModBiblioMarc
-
=cut
sub _koha_modify_biblioitem_nonmarc {
size = ?,
place = ?,
lccn = ?,
- marc = ?,
url = ?,
cn_source = ?,
cn_class = ?,
$biblioitem->{'volumedate'}, $biblioitem->{'volumedesc'}, $biblioitem->{'collectiontitle'}, $biblioitem->{'collectionissn'},
$biblioitem->{'collectionvolume'}, $biblioitem->{'editionstatement'}, $biblioitem->{'editionresponsibility'}, $biblioitem->{'illus'},
$biblioitem->{'pages'}, $biblioitem->{'bnotes'}, $biblioitem->{'size'}, $biblioitem->{'place'},
- $biblioitem->{'lccn'}, $biblioitem->{'marc'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'},
+ $biblioitem->{'lccn'}, $biblioitem->{'url'}, $biblioitem->{'biblioitems.cn_source'},
$biblioitem->{'cn_class'}, $biblioitem->{'cn_item'}, $biblioitem->{'cn_suffix'}, $cn_sort,
$biblioitem->{'totalissues'}, $biblioitem->{'ean'}, $biblioitem->{'agerestriction'}
);
my $sth = $dbh->prepare("SELECT * FROM biblio WHERE biblionumber=?");
$sth->execute($biblionumber);
+ # FIXME There is a transaction in _koha_delete_biblio_metadata
+ # But actually all the following should be done inside a single transaction
if ( my $data = $sth->fetchrow_hashref ) {
# save the record in deletedbiblio
$bkup_sth->execute(@bind);
$bkup_sth->finish;
+ _koha_delete_biblio_metadata( $biblionumber );
+
# delete the biblio
my $sth2 = $dbh->prepare("DELETE FROM biblio WHERE biblionumber=?");
$sth2->execute($biblionumber);
return;
}
+=head2 _koha_delete_biblio_metadata
+
+ $error = _koha_delete_biblio_metadata($biblionumber);
+
+C<$biblionumber> - the biblionumber of the biblio metadata to be deleted
+
+=cut
+
+sub _koha_delete_biblio_metadata {
+ my ($biblionumber) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $schema = Koha::Database->new->schema;
+ $schema->txn_do(
+ sub {
+ $dbh->do( q|
+ INSERT INTO deletedbiblio_metadata (biblionumber, format, marcflavour, metadata)
+ SELECT biblionumber, format, marcflavour, metadata FROM biblio_metadata WHERE biblionumber=?
+ |, undef, $biblionumber );
+ $dbh->do( q|DELETE FROM biblio_metadata WHERE biblionumber=?|,
+ undef, $biblionumber );
+ }
+ );
+}
+
=head1 UNEXPORTED FUNCTIONS
=head2 ModBiblioMarc
&ModBiblioMarc($newrec,$biblionumber,$frameworkcode);
-Add MARC data for a biblio to koha
+Add MARC XML data for a biblio to koha
Function exported, but should NOT be used, unless you really know what you're doing
sub ModBiblioMarc {
# pass the MARC::Record to this function, and it will create the records in
- # the marc field
+ # the marcxml field
my ( $record, $biblionumber, $frameworkcode ) = @_;
if ( !$record ) {
carp 'ModBiblioMarc passed an undefined record';
$f005->update(sprintf("%4d%02d%02d%02d%02d%04.1f",@a)) if $f005;
}
- $sth = $dbh->prepare("UPDATE biblioitems SET marc=?,marcxml=? WHERE biblionumber=?");
- $sth->execute( $record->as_usmarc(), $record->as_xml_record($encoding), $biblionumber );
- $sth->finish;
+ my $metadata = {
+ biblionumber => $biblionumber,
+ format => 'marcxml',
+ marcflavour => C4::Context->preference('marcflavour'),
+ };
+ # FIXME To replace with ->find_or_create?
+ if ( my $m_rs = Koha::Biblio::Metadatas->find($metadata) ) {
+ $m_rs->metadata( $record->as_xml_record($encoding) );
+ $m_rs->store;
+ } else {
+ my $m_rs = Koha::Biblio::Metadata->new($metadata);
+ $m_rs->metadata( $record->as_xml_record($encoding) );
+ $m_rs->store;
+ }
ModZebra( $biblionumber, "specialUpdate", "biblioserver", $record );
return $biblionumber;
}
return ($count);
}
-=head2 GetSubscriptionsId
-
- $subscriptions = &GetSubscriptionsId($biblionumber);
-
-This function return an array of subscriptionid with $biblionumber
-
-=cut
-
-sub GetSubscriptionsId {
- my ($biblionumber) = @_;
- my $dbh = C4::Context->dbh;
- my $query = "SELECT subscriptionid
- FROM subscription
- WHERE biblionumber=?";
- my $sth = $dbh->prepare($query);
- $sth->execute($biblionumber);
- my @subscriptions = $sth->fetchrow_array;
- return (@subscriptions);
-}
-
-=head2 GetHolds
-
- $holds = &GetHolds($biblionumber);
-
-This function return the count of holds with $biblionumber
-
-=cut
-
-sub GetHolds {
- my ($biblionumber) = @_;
- my $dbh = C4::Context->dbh;
- my $query = "SELECT count(*)
- FROM reserves
- WHERE biblionumber=?";
- my $sth = $dbh->prepare($query);
- $sth->execute($biblionumber);
- my $holds = $sth->fetchrow;
- return ($holds);
-}
-
=head2 prepare_host_field
$marcfield = prepare_host_field( $hostbiblioitem, $marcflavour );
carp "UpdateTotalIssues could not get biblio record";
return;
}
- my $data = GetBiblioData($biblionumber);
- unless ($data) {
+ my $biblio = Koha::Biblios->find( $biblionumber );
+ unless ($biblio) {
carp "UpdateTotalIssues could not get datas of biblio";
return;
}
- my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $data->{'frameworkcode'});
+ my $biblioitem = $biblio->biblioitem;
+ my ($totalissuestag, $totalissuessubfield) = GetMarcFromKohaField('biblioitems.totalissues', $biblio->frameworkcode);
unless ($totalissuestag) {
return 1; # There is nothing to do
}
if (defined $value) {
$totalissues = $value;
} else {
- $totalissues = $data->{'totalissues'} + $increase;
+ $totalissues = $biblioitem->totalissues + $increase;
}
my $field = $record->field($totalissuestag);
$record->insert_grouped_field($field);
}
- return ModBiblio($record, $biblionumber, $data->{'frameworkcode'});
+ return ModBiblio($record, $biblionumber, $biblio->frameworkcode);
}
=head2 RemoveAllNsb