Bug 31925: Add thesaurus/008 lookup and default MARC21 008 builder to Koha::Authority

AutoCreateAuthorities builds a brand-new authority record whenever a bib
subject heading (6XX) has no match, but always lets the record fall back
to C4::AuthoritiesMarc::AddAuthority()'s default 008/040, which hardcodes
008 position 11 ("Subject heading system/thesaurus") to 'a' (LCSH) no
matter what thesaurus the source heading actually uses. This is the first
step towards fixing that.

Adds two things to Koha::Authority:

- $MARC21_THESAURUS_TO_CONTROL_FIELD_008_11: a single-source-of-truth
  lookup mapping a thesaurus name (as already derived by C4::Heading from
  MARC indicator 2 / subfield $2, e.g. 'lcsh', 'mesh', or a raw $2 code
  such as 'fast') to its MARC21 authority 008/11 code, per the LOC ad008
  code list. This table previously only existed once, for a different
  purpose, in Koha::SearchEngine::Elasticsearch::QueryBuilder (used for
  building thesaurus search queries) - it's promoted here so both that
  and the authority-creation fix that follows can read from one place
  instead of two copies that can silently drift apart.

- default_marc21_008($thesaurus): builds the default 34-character body of
  a new MARC21 authority's 008 field, honouring the
  MARCAuthorityControlField008 system preference. When $thesaurus is
  given, position 11 is set from the table above instead of whatever the
  default/syspref value carries, falling back to 'z' (Other) for a
  thesaurus with no dedicated code.

AddAuthority() is refactored to build its own (thesaurus-less, LCSH
default) 008 via this new method too, replacing its previous inline copy
of the same defaulting logic - pure refactor, no behaviour change.
Nothing thesaurus-aware calls it yet.

Sponsored-by: Heythrop Library <https://heythroplibrary.co.uk/>
Signed-off-by: Clemens Gresser <cgresser@jesuit.org.uk>
Signed-off-by: Phil Ringnalda <phil@chetcolibrary.org>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Martin Renvoize 2026-07-10 12:11:32 +01:00 committed by Pedro Amorim
parent 9eaeae69e5
commit 4bfb9abc68
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
3 changed files with 113 additions and 11 deletions

View File

@ -620,16 +620,7 @@ sub AddAuthority {
}
my $date = POSIX::strftime( "%y%m%d", localtime );
if ( !$record->field('008') ) {
# Get a valid default value for field 008
my $default_008 = C4::Context->preference('MARCAuthorityControlField008');
if ( !$default_008 or length($default_008) < 34 ) {
$default_008 = '|| aca||aabn | a|a d';
} else {
$default_008 = substr( $default_008, 0, 34 );
}
$record->insert_fields_ordered( MARC::Field->new( '008', $date . $default_008 ) );
$record->insert_fields_ordered( MARC::Field->new( '008', $date . Koha::Authority->default_marc21_008 ) );
}
if ( !$record->field('040') ) {
$record->insert_fields_ordered(

View File

@ -21,6 +21,8 @@ use Modern::Perl;
use Scalar::Util qw( blessed );
use C4::Context;
use base qw(Koha::Object);
use Koha::Authority::ControlledIndicators;
@ -292,6 +294,77 @@ sub record_schema {
: 'MARC21';
}
=head3 $MARC21_THESAURUS_TO_CONTROL_FIELD_008_11
my $code = $Koha::Authority::MARC21_THESAURUS_TO_CONTROL_FIELD_008_11->{$thesaurus};
The mapping of a subject heading's thesaurus name (as derived by
C4::Heading from MARC indicator 2 / subfield $2, e.g. 'lcsh', 'mesh', or a
raw $2 code such as 'fast') to its MARC21 authority 008/11 code ("Subject
heading system/thesaurus"), per
L<https://www.loc.gov/marc/authority/ad008.html>.
Note that C<sears> and C<aat> map to 008/11 values here but never appear
as a thesaurus name derived by C4::Heading, because they don't have their
own MARC21 indicator 2 value (see the LOC list linked above).
=cut
our $MARC21_THESAURUS_TO_CONTROL_FIELD_008_11 = {
lcsh => 'a',
lcac => 'b',
mesh => 'c',
nal => 'd',
notapplicable => 'n',
cash => 'k',
rvm => 'v',
aat => 'r',
sears => 's',
notdefined => 'z',
notspecified => '|',
};
=head3 default_marc21_008
my $default_008 = Koha::Authority->default_marc21_008;
my $default_008 = Koha::Authority->default_marc21_008($thesaurus);
Returns the default 34-character body (i.e. everything after the
6-character date) to use for a new MARC21 authority record's 008 field,
honouring the I<MARCAuthorityControlField008> system preference and
falling back to Koha's hardcoded default when it isn't set to a valid (at
least 34 character) value.
If C<$thesaurus> is passed, position 11 ("Subject heading
system/thesaurus") is set from
L<$MARC21_THESAURUS_TO_CONTROL_FIELD_008_11> instead of whatever the
default/syspref value carries, falling back to 'z' (Other) for a
thesaurus with no dedicated code -- consistent with how Elasticsearch
indexing already reconciles 008/11 vs 040$f (see
Koha::SearchEngine::Elasticsearch::_process_mappings, where a 'z' is
dropped in favour of a genuine 040$f value).
=cut
sub default_marc21_008 {
my ( $class, $thesaurus ) = @_;
my $default_008 = C4::Context->preference('MARCAuthorityControlField008');
if ( !$default_008 or length($default_008) < 34 ) {
$default_008 = '|| aca||aabn | a|a d';
} else {
$default_008 = substr( $default_008, 0, 34 );
}
if ( defined $thesaurus ) {
# Position 11 is absolute (over date + body); within the 34-char body alone that's index 11 - 6 = 5.
substr( $default_008, 5, 1, $MARC21_THESAURUS_TO_CONTROL_FIELD_008_11->{$thesaurus} // 'z' );
}
return $default_008;
}
=head3 to_api_mapping
This method returns the mapping for representing a Koha::Authority object

View File

@ -20,7 +20,7 @@
use Modern::Perl;
use Test::MockModule;
use Test::More tests => 2;
use Test::More tests => 3;
use Test::NoWarnings;
use C4::AuthoritiesMarc;
@ -116,3 +116,41 @@ subtest 'move_to_deleted' => sub {
$schema->storage->txn_rollback;
};
subtest 'default_marc21_008 tests (bug 31925)' => sub {
plan tests => 7;
is(
substr( Koha::Authority->default_marc21_008('lcsh'), 5, 1 ), 'a',
'lcsh maps to 008/11 code a'
);
is(
substr( Koha::Authority->default_marc21_008('mesh'), 5, 1 ), 'c',
'mesh maps to 008/11 code c'
);
is(
substr( Koha::Authority->default_marc21_008('rvm'), 5, 1 ), 'v',
'rvm maps to 008/11 code v'
);
is(
substr( Koha::Authority->default_marc21_008('fast'), 5, 1 ), 'z',
'Unrecognized raw $2 code (e.g. fast, from an ind2=7 heading) maps to 008/11 code z (Other)'
);
t::lib::Mocks::mock_preference( 'MARCAuthorityControlField008', '' );
is(
Koha::Authority->default_marc21_008, '|| aca||aabn | a|a d',
'Falls back to the hardcoded default when the syspref is unset, and thesaurus is not passed'
);
is(
substr( Koha::Authority->default_marc21_008('fast'), 5, 1 ), 'z',
'Thesaurus code is still applied on top of the hardcoded default'
);
my $custom = '||caaa||aabn | a|a d##padding';
t::lib::Mocks::mock_preference( 'MARCAuthorityControlField008', $custom );
is(
Koha::Authority->default_marc21_008, substr( $custom, 0, 34 ),
'Uses the syspref value, truncated to 34 characters, when valid'
);
};