Bug 42567: Remove C4::Koha::GetNormalizedEAN in favor of MetadataExtractor

Adds get_normalized_ean to the flavor-specific extractors:
- MARC21: 024$a with indicator 1 = 3
- UNIMARC: 073$a

Exposes it as $biblio->normalized_ean and migrates all callers.

Assisted-by: Sonnet 4.6 (Anthropic)

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/Koha.t \
          t/db_dependent/Koha/Biblio.t \
          t/db_dependent/Koha/Biblio/Metadata/Extractor/MARC/MARC21.t \
          t/db_dependent/Koha/Biblio/Metadata/Extractor/MARC/UNIMARC.t
=> SUCCESS: Tests pass
3. Verify EAN numbers still display in OPAC and staff detail pages
4. Sign off :-D

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Tomás Cohen Arazi 2026-05-08 15:57:36 -03:00 committed by Pedro Amorim
parent 3d3f625b56
commit 3522dc2bca
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
16 changed files with 149 additions and 62 deletions

View File

@ -34,7 +34,6 @@ BEGIN {
getitemtypeimagelocation
GetAuthorisedValues
GetNormalizedISBN
GetNormalizedEAN
xml_escape
GetVariationsOfISBN
@ -639,37 +638,6 @@ sub GetNormalizedISBN {
}
}
=head2 GetNormalizedEAN
Missing POD for GetNormalizedEAN.
=cut
sub GetNormalizedEAN {
my ( $marcrecord, $marcflavour ) = @_;
return unless $marcrecord;
if ( $marcflavour eq 'UNIMARC' ) {
my @fields = $marcrecord->field('073');
foreach my $field (@fields) {
my $ean = _normalize_match_point( $field->subfield('a') );
if ($ean) {
return $ean;
}
}
} else { # assume marc21 if not unimarc
my @fields = $marcrecord->field('024');
foreach my $field (@fields) {
my $indicator = $field->indicator(1);
my $ean = _normalize_match_point( $field->subfield('a') );
if ( $ean && $indicator eq '3' ) {
return $ean;
}
}
}
}
sub _normalize_match_point {
my $match_point = shift;
( my $normalized_match_point ) = $match_point =~ /([\d-]*[X]*)/;

View File

@ -35,7 +35,7 @@ BEGIN {
use C4::Context;
use C4::Biblio qw( TransformMarcToKoha GetMarcFromKohaField GetFrameworkCode GetAuthorisedValueDesc GetBiblioData );
use C4::Koha qw( getFacets GetVariationsOfISBN GetNormalizedEAN GetNormalizedISBN getitemtypeimagelocation );
use C4::Koha qw( getFacets GetVariationsOfISBN GetNormalizedISBN getitemtypeimagelocation );
use Koha::Biblio::Metadata::Extractor;
use Koha::DateUtils;
use Koha::Libraries;
@ -1770,7 +1770,7 @@ sub searchResults {
my $extractor = Koha::Biblio::Metadata::Extractor->new( { metadata => $marcrecord } );
$oldbiblio->{normalized_upc} = $extractor->get_normalized_upc;
$oldbiblio->{normalized_ean} = GetNormalizedEAN( $marcrecord, $marcflavour );
$oldbiblio->{normalized_ean} = $extractor->get_normalized_ean;
$oldbiblio->{normalized_oclc} = $extractor->get_normalized_oclc;
$oldbiblio->{normalized_isbn} = GetNormalizedISBN( $oldbiblio->{isbn}, $marcrecord, $marcflavour )
; # Use existing ISBN from record if we got one

View File

@ -28,7 +28,7 @@ BEGIN {
use C4::Biblio qw( GetAuthorisedValueDesc );
use C4::Context;
use C4::Koha qw( GetNormalizedISBN GetNormalizedEAN );
use C4::Koha qw( GetNormalizedISBN );
use Koha::Biblios;
use Koha::Libraries;
@ -238,7 +238,7 @@ sub GetShelfInfo {
$item->{'browser_normalized_upc'} = $biblio->normalized_upc;
$item->{'browser_normalized_oclc'} = $biblio->normalized_oclc;
$item->{'browser_normalized_isbn'} = GetNormalizedISBN( undef, $this_record, $marcflavour );
$item->{'browser_normalized_ean'} = GetNormalizedEAN( $this_record, $marcflavour );
$item->{'browser_normalized_ean'} = $biblio->normalized_ean;
push @valid_items, $item;
}
return @valid_items;

View File

@ -1703,6 +1703,19 @@ sub normalized_upc {
return $self->metadata_extractor->get_normalized_upc;
}
=head3 normalized_ean
my $normalized_ean = $biblio->normalized_ean
Normalizes and returns the EAN found in the MARC record.
=cut
sub normalized_ean {
my ($self) = @_;
return $self->metadata_extractor->get_normalized_ean;
}
=head3 normalized_oclc
my $normalized_oclc = $biblio->normalized_oclc

View File

@ -74,6 +74,29 @@ sub get_normalized_upc {
}
}
=head2 get_normalized_ean
my $normalized_ean = $extractor->get_normalized_ean();
Returns a normalized EAN from 024$a with indicator 1 = 3.
=cut
sub get_normalized_ean {
my ($self) = @_;
my $record = $self->metadata;
my @fields = $record->field('024');
foreach my $field (@fields) {
my $indicator = $field->indicator(1);
my $ean = $self->_normalize_string( $field->subfield('a') );
if ( $ean && $indicator eq '3' ) {
return $ean;
}
}
return;
}
=head2 check_fixed_length
my $info = $extractor->check_fixed_length;

View File

@ -72,6 +72,28 @@ sub get_normalized_upc {
}
}
=head2 get_normalized_ean
my $normalized_ean = $extractor->get_normalized_ean();
Returns a normalized EAN from 073$a.
=cut
sub get_normalized_ean {
my ($self) = @_;
my $record = $self->metadata;
my @fields = $record->field('073');
foreach my $field (@fields) {
my $ean = $self->_normalize_string( $field->subfield('a') );
if ($ean) {
return $ean;
}
}
return;
}
=head1 AUTHOR
Tomas Cohen Arazi, E<lt>tomascohen@theke.ioE<gt>

View File

@ -23,7 +23,6 @@ use C4::Context;
use C4::Koha qw(
GetAuthorisedValues
getitemtypeimagelocation
GetNormalizedEAN
GetNormalizedISBN
);
use C4::Serials qw( CountSubscriptionFromBiblionumber SearchSubscriptions GetLatestSerials );
@ -168,7 +167,7 @@ $template->param( ocoins => !$invalid_marc_record ? $biblio->get_coins : undef )
# in each case, we're grabbing the first value we find in
# the record and normalizing it
my $upc = $biblio->normalized_upc;
my $ean = GetNormalizedEAN( $marc_record, $marcflavour );
my $ean = $biblio->normalized_ean;
my $oclc = $biblio->normalized_oclc;
my $isbn = GetNormalizedISBN( undef, $marc_record, $marcflavour );
my $content_identifier_exists;

View File

@ -54,7 +54,6 @@ use C4::Record qw( marc2cites );
use C4::Reserves qw( IsAvailableForItemLevelRequest );
use C4::Serials qw( CountSubscriptionFromBiblionumber SearchSubscriptions GetLatestSerials );
use C4::Koha qw(
GetNormalizedEAN
GetNormalizedISBN
);
use Koha::CirculationRules;
@ -131,7 +130,7 @@ my $marcflavour = C4::Context->preference("marcflavour");
# in each case, we're grabbing the first value we find in
# the record and normalizing it
my $upc = $biblio->normalized_upc;
my $ean = GetNormalizedEAN( $record, $marcflavour );
my $ean = $extractor->get_normalized_ean;
my $oclc = $extractor->get_normalized_oclc;
my $isbn = GetNormalizedISBN( undef, $record, $marcflavour );
my $content_identifier_exists;

View File

@ -26,7 +26,6 @@ use C4::Acquisition qw( SearchOrders );
use C4::Auth qw( get_template_and_user get_session );
use C4::Koha qw(
getitemtypeimagelocation
GetNormalizedEAN
GetNormalizedISBN
);
use C4::Search qw( new_record_from_zebra searchResults getRecords );
@ -159,7 +158,7 @@ if ( $query->cookie("bib_list") ) {
SetUTF8Flag($record);
my $marcflavour = C4::Context->preference("marcflavour");
my $ean = GetNormalizedEAN( $record, $marcflavour );
my $ean = $biblio->normalized_ean;
my $OpacBrowseResults = C4::Context->preference("OpacBrowseResults");

View File

@ -23,7 +23,6 @@ use CGI qw ( -utf8 );
use C4::Auth qw( get_template_and_user );
use C4::Biblio qw( GetBiblioData GetFrameworkCode );
use C4::Koha qw(
GetNormalizedEAN
GetNormalizedISBN
);
use C4::Members;
@ -388,12 +387,11 @@ if ( $op eq 'view' ) {
$this_item->{description} = $itemtype->description; #FIXME Should not it be translated_description?
$this_item->{notforloan} = $itemtype->notforloan;
}
my $extractor = Koha::Biblio::Metadata::Extractor->new( { metadata => $record } );
$this_item->{'coins'} = $biblio->get_coins;
$this_item->{'normalized_upc'} =
Koha::Biblio::Metadata::Extractor->new( { metadata => $record } )->get_normalized_upc;
$this_item->{'normalized_ean'} = GetNormalizedEAN( $record, $marcflavour );
$this_item->{'normalized_oclc'} =
Koha::Biblio::Metadata::Extractor->new( { metadata => $record } )->get_normalized_oclc;
$this_item->{'normalized_upc'} = $extractor->get_normalized_upc;
$this_item->{'normalized_ean'} = $extractor->get_normalized_ean;
$this_item->{'normalized_oclc'} = $extractor->get_normalized_oclc;
$this_item->{'normalized_isbn'} = GetNormalizedISBN( undef, $record, $marcflavour );
# BZ17530: 'Intelligent' guess if result can be article requested

View File

@ -23,7 +23,6 @@ use Modern::Perl;
use CGI qw ( -utf8 );
use C4::Auth qw( get_template_and_user );
use C4::Koha qw(
GetNormalizedEAN
GetNormalizedISBN
);
use C4::Output qw( output_html_with_http_headers );
@ -93,8 +92,8 @@ for my $result (@$reviews) {
my $biblioitem = $biblio->biblioitem;
my $record = $biblio->metadata->record;
$result->{normalized_upc} = $biblio->normalized_upc;
$result->{normalized_ean} = GetNormalizedEAN( $record, $marcflavour );
$result->{normalized_oclc} = Koha::Biblio::Metadata::Extractor->new( { metadata => $record } )->get_normalized_oclc;
$result->{normalized_ean} = $biblio->normalized_ean;
$result->{normalized_oclc} = $biblio->normalized_oclc;
$result->{normalized_isbn} = GetNormalizedISBN( undef, $record, $marcflavour );
$result->{title} = $biblio->title;
$result->{subtitle} = $biblio->subtitle;

View File

@ -173,7 +173,7 @@ subtest 'Authorized Values Tests' => sub {
};
subtest 'isbn tests' => sub {
plan tests => 31;
plan tests => 30;
my $isbn13 = "9780330356473";
my $isbn13D = "978-0-330-35647-3";
@ -283,10 +283,6 @@ subtest 'isbn tests' => sub {
C4::Koha::GetNormalizedISBN(), undef,
'GetNormalizedISBN should return undef if no record and no isbn are passed'
);
is(
C4::Koha::GetNormalizedEAN(), undef,
'GetNormalizedEAN should return undef if no record and no isbn are passed'
);
is( C4::Koha::GetNormalizedISBN('979-8-88650-962-5'), '9798886509625', 'Test GetNormalizedISBN' );

View File

@ -18,7 +18,7 @@
use Modern::Perl;
use Test::NoWarnings;
use Test::More tests => 43;
use Test::More tests => 44;
use Test::Exception;
use Test::Warn;
@ -2037,6 +2037,28 @@ subtest 'normalized_upc() tests' => sub {
};
subtest 'normalized_ean() tests' => sub {
plan tests => 2;
$schema->storage->txn_begin;
t::lib::Mocks::mock_preference( 'marcflavour', 'MARC21' );
my $biblio = $builder->build_sample_biblio();
my $record = $biblio->metadata->record;
$record->append_fields( MARC::Field->new( '024', '3', ' ', a => '978-3-16-148410-0' ) );
C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
$biblio = $biblio->get_from_storage;
is( $biblio->normalized_ean, '9783161484100', 'normalized_ean returns EAN from 024 ind1=3' );
my $biblio_no_ean = $builder->build_sample_biblio();
is( $biblio_no_ean->normalized_ean, undef, 'normalized_ean returns undef when no EAN' );
$schema->storage->txn_rollback;
};
subtest 'normalized_oclc() tests' => sub {
plan tests => 2;

View File

@ -20,7 +20,7 @@
use Modern::Perl;
use Test::NoWarnings;
use Test::More tests => 5;
use Test::More tests => 6;
use Test::Exception;
use t::lib::TestBuilder;
@ -140,3 +140,31 @@ subtest 'check_fixed_length' => sub {
$schema->storage->txn_rollback;
};
subtest 'get_normalized_ean() tests' => sub {
plan tests => 3;
$schema->storage->txn_begin;
my $record = MARC::Record->new();
$record->append_fields(
MARC::Field->new( '024', '1', ' ', a => '123456789' ), # UPC (ind1=1), not EAN
MARC::Field->new( '024', '3', ' ', a => '978-3-16-148410-0' ), # EAN (ind1=3)
);
my $extractor = Koha::Biblio::Metadata::Extractor->new( { metadata => $record } );
is( $extractor->get_normalized_ean, '9783161484100', 'EAN extracted and normalized (dashes removed)' );
$record = MARC::Record->new();
$record->append_fields(
MARC::Field->new( '024', '1', ' ', a => '123456789' ), # UPC only
);
$extractor = Koha::Biblio::Metadata::Extractor->new( { metadata => $record } );
is( $extractor->get_normalized_ean, undef, 'Returns undef when no EAN (ind1=3) present' );
$record = MARC::Record->new();
$extractor = Koha::Biblio::Metadata::Extractor->new( { metadata => $record } );
is( $extractor->get_normalized_ean, undef, 'Returns undef for empty record' );
$schema->storage->txn_rollback;
};

View File

@ -20,7 +20,7 @@
use Modern::Perl;
use Test::NoWarnings;
use Test::More tests => 3;
use Test::More tests => 4;
use Test::Exception;
use t::lib::TestBuilder;
@ -76,3 +76,25 @@ subtest 'get_normalized_upc() tests' => sub {
$schema->storage->txn_rollback;
};
subtest 'get_normalized_ean() tests' => sub {
plan tests => 2;
$schema->storage->txn_begin;
t::lib::Mocks::mock_preference( 'marcflavour', 'UNIMARC' );
my $record = MARC::Record->new();
$record->append_fields(
MARC::Field->new( '073', ' ', ' ', a => '978-2-07-036822-8' ),
);
my $extractor = Koha::Biblio::Metadata::Extractor::MARC->new( { metadata => $record } );
is( $extractor->get_normalized_ean, '9782070368228', 'EAN extracted from 073$a and normalized' );
$record = MARC::Record->new();
$extractor = Koha::Biblio::Metadata::Extractor::MARC->new( { metadata => $record } );
is( $extractor->get_normalized_ean, undef, 'Returns undef for empty record' );
$schema->storage->txn_rollback;
};

View File

@ -23,7 +23,6 @@ use C4::Auth qw( get_template_and_user haspermission );
use C4::Circulation qw( barcodedecode );
use C4::Context;
use C4::Koha qw(
GetNormalizedEAN
GetNormalizedISBN
);
use C4::Members;
@ -352,7 +351,7 @@ if ( $op eq 'view' ) {
$this_item->{notforloan} = $itemtype->notforloan if $itemtype;
$this_item->{'coins'} = $biblio->get_coins;
$this_item->{'normalized_upc'} = $biblio->normalized_upc;
$this_item->{'normalized_ean'} = GetNormalizedEAN( $record, $marcflavour );
$this_item->{'normalized_ean'} = $biblio->normalized_ean;
$this_item->{'normalized_oclc'} = $biblio->normalized_oclc;
$this_item->{'normalized_isbn'} = GetNormalizedISBN( undef, $record, $marcflavour );