diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm index c2c1d02fbb..5402dec09b 100644 --- a/C4/AuthoritiesMarc.pm +++ b/C4/AuthoritiesMarc.pm @@ -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( diff --git a/Koha/Authority.pm b/Koha/Authority.pm index b110c871d4..d3f76ff106 100644 --- a/Koha/Authority.pm +++ b/Koha/Authority.pm @@ -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. + +Note that C and C 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 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 diff --git a/t/db_dependent/Koha/Authority.t b/t/db_dependent/Koha/Authority.t index 6ea0d32127..e19bb56e39 100755 --- a/t/db_dependent/Koha/Authority.t +++ b/t/db_dependent/Koha/Authority.t @@ -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' + ); +};