From 592a12061d76b72fd029012fdd12d83deae3a694 Mon Sep 17 00:00:00 2001
From: Julian Maurice
Date: Wed, 13 Nov 2024 16:55:25 +0100
Subject: [PATCH] Bug 15248: Use record matching rules to find duplicate while
cataloging
This patch allows to configure how Koha searches for duplicates when
cataloging manually by using a record matching rule.
For each framework we can now set a record matching rule to use when
searching for duplicates.
This allow to use different matching rules while in acquisition for
instance (ACQ framework).
If a matching rule is selected, we also have the ability to forbid the
creation of duplicate: if a duplicate is found and this option is
enabled, the usual option to create a duplicate will be hidden.
If no matching rule is selected, then the default matching mechanism is
used.
Test plan:
1. Create a record matching rule matching on ISBN
Threshold: 100
Record type: biblio
Match point:
Search index: "isbn"
Score: 100
Tag: "020" (or "010" for UNIMARC)
Subfields: "a"
Offset: 0
Length: 0
Normalization rule: Remove spaces
No match checks
2. Edit the default MARC framework and select the new matching rule and
enable "Forbid duplicate creation"
3. Create a biblio with an ISBN
4. Make sure your new biblio is indexed in Zebra or ES
5. Try to create a biblio with the same ISBN. You should see a warning
about the duplicate with no option to create the duplicate biblio
6. In the acquisitions module, in a basket add a new order from an
external source (requires a configured Z39.50/SRU server)
7. Choose a record with an ISBN
8. Once the order is created, try to create a new order from an external
source and choose the same record. You should see a warning about the
duplicate.
9. Edit the record matching rule and disable "Forbid duplicate creation"
10. Redo step 5. Now there should be an option to create the duplicate
record.
11. Redo step 8. Now there should be an option to create the duplicate
record.
12. Run the following test scripts:
- t/db_dependent/Search.t
- t/db_dependent/Matcher.t
Sponsored-by: SCD Lyon2
Signed-off-by: Andrew Fuerste Henry
Signed-off-by: Phillip Berg
---
C4/Matcher.pm | 40 +++---
C4/Search.pm | 43 ++++++
Koha/BiblioFramework.pm | 17 +++
Koha/BiblioFrameworkMarcMatcher.pm | 43 ++++++
Koha/BiblioFrameworkMarcMatchers.pm | 49 +++++++
Koha/REST/V1/Biblios.pm | 11 +-
acqui/neworderempty.pl | 55 ++++----
admin/biblio_framework.pl | 77 +++++++----
cataloguing/addbiblio.pl | 12 +-
.../data/mysql/atomicupdate/bug-15248.pl | 29 ++++
installer/data/mysql/kohastructure.sql | 17 +++
.../modules/acqui/neworderempty_duplicate.tt | 30 +++--
.../prog/en/modules/admin/biblio_framework.tt | 38 +++++-
.../prog/en/modules/admin/matching-rules.tt | 2 +
.../prog/en/modules/cataloguing/addbiblio.tt | 126 +++++++++++-------
t/db_dependent/Matcher.t | 6 +-
t/db_dependent/Search.t | 75 ++++++++++-
17 files changed, 521 insertions(+), 149 deletions(-)
create mode 100644 Koha/BiblioFrameworkMarcMatcher.pm
create mode 100644 Koha/BiblioFrameworkMarcMatchers.pm
create mode 100644 installer/data/mysql/atomicupdate/bug-15248.pl
diff --git a/C4/Matcher.pm b/C4/Matcher.pm
index 0aaaf818d9b..41d2c6512a9 100644
--- a/C4/Matcher.pm
+++ b/C4/Matcher.pm
@@ -19,6 +19,7 @@ package C4::Matcher;
use Modern::Perl;
+use Koha::Database;
use Koha::SearchEngine;
use Koha::SearchEngine::Search;
use Koha::SearchEngine::QueryBuilder;
@@ -70,7 +71,7 @@ C4::Matcher - find MARC records matching another one
=head2 GetMatcherList
- my @matchers = C4::Matcher::GetMatcherList();
+ my @matchers = C4::Matcher::GetMatcherList($filters);
Returns an array of hashrefs list all matchers
present in the database. Each hashref includes:
@@ -78,19 +79,32 @@ present in the database. Each hashref includes:
* matcher_id
* code
* description
+ * record_type
+
+C<$filters> is an optional hashref parameter that allows to filter the result. Useful keys are:
+
+=over
+
+=item * C
+
+=back
+
+See L for more info
+
+=head3 Examples
+
+ @matchers = C4::Matcher::GetMatcherList();
+ @matchers = C4::Matcher::GetMatcherList({ record_type => 'biblio' });
+ @matchers = C4::Matcher::GetMatcherList({ record_type => 'authority' });
=cut
sub GetMatcherList {
- my $dbh = C4::Context->dbh;
+ my ($filters) = @_;
- my $sth = $dbh->prepare_cached("SELECT matcher_id, code, description FROM marc_matchers ORDER BY matcher_id");
- $sth->execute();
- my @results = ();
- while ( my $row = $sth->fetchrow_hashref ) {
- push @results, $row;
- }
- return @results;
+ my $rs = Koha::Database->schema->resultset('MarcMatcher');
+
+ return $rs->search($filters // {}, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' })->all;
}
=head2 GetMatcherId
@@ -171,12 +185,8 @@ sub fetch {
$sth->finish();
return unless defined $row;
- my $self = {};
- $self->{'id'} = $row->{'matcher_id'};
- $self->{'record_type'} = $row->{'record_type'};
- $self->{'code'} = $row->{'code'};
- $self->{'description'} = $row->{'description'};
- $self->{'threshold'} = int( $row->{'threshold'} );
+ my $self = { %$row };
+ $self->{id} = delete $self->{matcher_id};
bless $self, $class;
# matchpoints
diff --git a/C4/Search.pm b/C4/Search.pm
index 12ba64f60fe..4cb9e121daa 100644
--- a/C4/Search.pm
+++ b/C4/Search.pm
@@ -28,7 +28,9 @@ use XML::Simple;
use C4::XSLT qw( XSLTParse4Display );
use C4::Reserves qw( GetReserveStatus );
use C4::Charset qw( SetUTF8Flag );
+use C4::Matcher;
use Koha::AuthorisedValues;
+use Koha::BiblioFrameworkMarcMatchers;
use Koha::ItemTypes;
use Koha::Libraries;
use Koha::Logger;
@@ -146,6 +148,47 @@ sub FindDuplicate {
return @results;
}
+sub FindDuplicateWithMatchingRules {
+ my ($record, $frameworkcode) = @_;
+
+ $frameworkcode //= '';
+
+ my $biblio_framework_marc_matcher = Koha::BiblioFrameworkMarcMatchers->find($frameworkcode);
+ if ($biblio_framework_marc_matcher) {
+ my $matcher = C4::Matcher->fetch($biblio_framework_marc_matcher->marc_matcher_id);
+ if ($matcher) {
+ my @duplicates;
+ my $max_matches = 1;
+ my @matches = $matcher->get_matches($record, $max_matches);
+ foreach my $match (@matches) {
+ my $biblio = Koha::Biblios->find($match->{record_id});
+ if ($biblio) {
+ push @duplicates, {
+ biblionumber => $biblio->biblionumber,
+ title => $biblio->title,
+ forbid_duplicate_creation => $biblio_framework_marc_matcher->forbid_duplicate_creation,
+ };
+ }
+ }
+
+ return @duplicates;
+ }
+ }
+
+ # If no matcher can be used, default to FindDuplicate
+ my @results = FindDuplicate($record);
+ my @duplicates;
+ while ((my $biblionumber = shift @results) && (my $title = shift @results)) {
+ push @duplicates, {
+ biblionumber => $biblionumber,
+ title => $title,
+ forbid_duplicate_creation => 0,
+ }
+ }
+
+ return @duplicates;
+}
+
=head2 SimpleSearch
( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers], [%options] );
diff --git a/Koha/BiblioFramework.pm b/Koha/BiblioFramework.pm
index 02d08c98325..bc661234cb3 100644
--- a/Koha/BiblioFramework.pm
+++ b/Koha/BiblioFramework.pm
@@ -31,6 +31,23 @@ Koha::BiblioFramework - Koha BiblioFramework Object class
=cut
+=head3 delete
+
+See L
+
+=cut
+
+sub delete {
+ my ($self) = @_;
+
+ my $biblio_framework_marc_matcher = Koha::BiblioFrameworkMarcMatchers->find($self->frameworkcode);
+ if ($biblio_framework_marc_matcher) {
+ $biblio_framework_marc_matcher->delete();
+ }
+
+ $self->SUPER::delete();
+}
+
=head3 type
=cut
diff --git a/Koha/BiblioFrameworkMarcMatcher.pm b/Koha/BiblioFrameworkMarcMatcher.pm
new file mode 100644
index 00000000000..c02c09a61f9
--- /dev/null
+++ b/Koha/BiblioFrameworkMarcMatcher.pm
@@ -0,0 +1,43 @@
+package Koha::BiblioFrameworkMarcMatcher;
+
+# 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 3 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, see .
+
+use Modern::Perl;
+
+
+use Koha::Database;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::BiblioFrameworkMarcMatcher
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+ return 'BiblioFrameworkMarcMatcher';
+}
+
+1;
diff --git a/Koha/BiblioFrameworkMarcMatchers.pm b/Koha/BiblioFrameworkMarcMatchers.pm
new file mode 100644
index 00000000000..32bef344b27
--- /dev/null
+++ b/Koha/BiblioFrameworkMarcMatchers.pm
@@ -0,0 +1,49 @@
+package Koha::BiblioFrameworkMarcMatchers;
+
+# 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 3 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, see .
+
+use Modern::Perl;
+
+
+use Koha::Database;
+
+use Koha::BiblioFrameworkMarcMatcher;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::BiblioFrameworkMarcMatchers
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+ return 'BiblioFrameworkMarcMatcher';
+}
+
+sub object_class {
+ return 'Koha::BiblioFrameworkMarcMatcher';
+}
+
+1;
diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm
index 94bca50e83b..3bbb6387a55 100644
--- a/Koha/REST/V1/Biblios.pm
+++ b/Koha/REST/V1/Biblios.pm
@@ -23,7 +23,7 @@ use Koha::Biblios;
use Koha::DateUtils;
use Koha::Ratings;
use C4::Biblio qw( DelBiblio AddBiblio ModBiblio );
-use C4::Search qw( FindDuplicate );
+use C4::Search;
use C4::Auth qw( haspermission );
use C4::Barcodes::ValueBuilder;
@@ -667,15 +667,14 @@ sub add {
my $confirm_not_duplicate = $headers->header('x-confirm-not-duplicate');
- if ( !$confirm_not_duplicate ) {
- my ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($record);
-
+ my ($duplicate) = C4::Search::FindDuplicateWithMatchingRules($record, $frameworkcode);
+ if ( $duplicate && ( $duplicate->{forbid_duplicate_creation} || !$confirm_not_duplicate ) ) {
return $c->render(
status => 400,
openapi => {
- error => "Duplicate biblio $duplicatebiblionumber",
+ error => "Duplicate biblio $duplicate->{biblionumber}",
}
- ) if $duplicatebiblionumber;
+ );
}
my ($biblio_id) = C4::Biblio::AddBiblio( $record, $frameworkcode, { record_source_id => $record_source_id } );
diff --git a/acqui/neworderempty.pl b/acqui/neworderempty.pl
index c4db7f4044b..25b0ad6edba 100755
--- a/acqui/neworderempty.pl
+++ b/acqui/neworderempty.pl
@@ -83,7 +83,7 @@ use C4::Biblio qw(
);
use C4::Output qw( output_and_exit output_html_with_http_headers );
use C4::Members;
-use C4::Search qw( FindDuplicate );
+use C4::Search;
#needed for z3950 import:
use C4::ImportBatch qw( SetImportRecordStatus SetMatchedBiblionumber GetImportRecordMarc );
@@ -172,15 +172,31 @@ if ( $ordernumber eq '' and defined $breedingid ) {
$marcrecord->delete_field($item);
}
- my $duplicatetitle;
-
- #look for duplicates
- ( $biblionumber, $duplicatetitle ) = FindDuplicate($marcrecord);
- if ( $biblionumber && $op ne 'cud-use_external_source' ) {
-
+ # look for duplicates
+ my ($duplicate) = C4::Search::FindDuplicateWithMatchingRules($marcrecord, $frameworkcode);
+ if ( $duplicate && $op ne 'cud-use_external_source' ) {
#if duplicate record found and user did not decide yet, first warn user
#and let them choose between using a new record or an existing record
- Load_Duplicate($duplicatetitle);
+ #(if the matching rule does not forbid it)
+ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+ {
+ template_name => "acqui/neworderempty_duplicate.tt",
+ query => $input,
+ type => "intranet",
+ flagsrequired => { acquisition => 'order_manage' },
+ }
+ );
+
+ my $marcflavour = uc C4::Context->preference("marcflavour");
+ $template->param(
+ basketno => $basketno,
+ booksellerid => $basket->{'booksellerid'},
+ breedingid => $breedingid,
+ duplicate => $duplicate,
+ $marcflavour => 1
+ );
+
+ output_html_with_http_headers $input, $cookie, $template->output;
exit;
}
@@ -608,26 +624,3 @@ sub MARCfindbreeding {
}
return -1;
}
-
-sub Load_Duplicate {
- my ($duplicatetitle) = @_;
- ( $template, $loggedinuser, $cookie ) = get_template_and_user(
- {
- template_name => "acqui/neworderempty_duplicate.tt",
- query => $input,
- type => "intranet",
- flagsrequired => { acquisition => 'order_manage' },
- }
- );
-
- $template->param(
- biblionumber => $biblionumber,
- basketno => $basketno,
- booksellerid => $basket->{'booksellerid'},
- breedingid => $breedingid,
- duplicatetitle => $duplicatetitle,
- ( uc( C4::Context->preference("marcflavour") ) ) => 1
- );
-
- output_html_with_http_headers $input, $cookie, $template->output;
-}
diff --git a/admin/biblio_framework.pl b/admin/biblio_framework.pl
index ecad5c2bb2b..eb7709c8b37 100755
--- a/admin/biblio_framework.pl
+++ b/admin/biblio_framework.pl
@@ -23,13 +23,17 @@ use CGI qw ( -utf8 );
use C4::Context;
use C4::Auth qw( get_template_and_user );
use C4::Output qw( output_html_with_http_headers );
+use C4::Matcher;
use Koha::Biblios;
use Koha::BiblioFramework;
use Koha::BiblioFrameworks;
+use Koha::BiblioFrameworkMarcMatcher;
+use Koha::BiblioFrameworkMarcMatchers;
use Koha::Caches;
+use Koha::I18N;
my $input = CGI->new;
-my $frameworkcode = $input->param('frameworkcode') || q||;
+my $frameworkcode = $input->param('frameworkcode');
my $op = $input->param('op') || q|list|;
my $cache = Koha::Caches->get_instance();
my @messages;
@@ -46,38 +50,59 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
my $dbh = C4::Context->dbh;
if ( $op eq 'add_form' ) {
my $framework;
- if ($frameworkcode) {
- $framework = Koha::BiblioFrameworks->find($frameworkcode);
+ my $biblio_framework_marc_matcher;
+ if (defined $frameworkcode) {
+ if ($frameworkcode ne '') {
+ $framework = Koha::BiblioFrameworks->find($frameworkcode);
+ } else {
+ $framework = Koha::BiblioFramework->new({ frameworkcode => '', frameworktext => __('Default framework') })
+ }
+
+ $biblio_framework_marc_matcher = Koha::BiblioFrameworkMarcMatchers->find($frameworkcode);
}
$template->param( framework => $framework );
+ $template->param( biblio_framework_marc_matcher => $biblio_framework_marc_matcher );
+
+ my @matchers = C4::Matcher::GetMatcherList();
+ $template->param('marc_matchers' => \@matchers);
} elsif ( $op eq 'cud-add_validate' ) {
- my $frameworkcode = $input->param('frameworkcode');
- my $frameworktext = $input->param('frameworktext');
- my $is_a_modif = $input->param('is_a_modif');
-
- if ($is_a_modif) {
- my $framework = Koha::BiblioFrameworks->find($frameworkcode);
- $framework->frameworktext($frameworktext);
- eval { $framework->store; };
- if ($@) {
- push @messages, { type => 'error', code => 'error_on_update' };
- } else {
- push @messages, { type => 'message', code => 'success_on_update' };
- }
+ my $frameworkcode = $input->param('frameworkcode');
+ my $frameworktext = $input->param('frameworktext');
+ my $marc_matcher_id = $input->param('marc_matcher_id');
+ my $forbid_duplicate_creation = $input->param('forbid_duplicate_creation') // 0;
+ my $is_a_modif = $input->param('is_a_modif');
+
+ my $framework;
+ if ( $is_a_modif && $frameworkcode ne '' ) {
+ $framework = Koha::BiblioFrameworks->find($frameworkcode);
} else {
- my $framework = Koha::BiblioFramework->new(
- {
- frameworkcode => $frameworkcode,
- frameworktext => $frameworktext,
+ $framework = Koha::BiblioFramework->new( { frameworkcode => $frameworkcode } );
+ }
+
+ $framework->frameworktext($frameworktext);
+ eval {
+ $framework->store() unless $framework->frameworkcode eq '';
+
+ my $biblio_framework_marc_matcher = Koha::BiblioFrameworkMarcMatchers->find($frameworkcode);
+ if ($marc_matcher_id) {
+ unless ($biblio_framework_marc_matcher) {
+ $biblio_framework_marc_matcher =
+ Koha::BiblioFrameworkMarcMatcher->new( { frameworkcode => $frameworkcode } );
}
- );
- eval { $framework->store; };
- if ($@) {
- push @messages, { type => 'error', code => 'error_on_insert' };
- } else {
- push @messages, { type => 'message', code => 'success_on_insert' };
+ $biblio_framework_marc_matcher->marc_matcher_id($marc_matcher_id);
+ $biblio_framework_marc_matcher->forbid_duplicate_creation($forbid_duplicate_creation);
+ $biblio_framework_marc_matcher->store();
+ } elsif ($biblio_framework_marc_matcher) {
+ $biblio_framework_marc_matcher->delete();
}
+ };
+
+ if ($@) {
+ push @messages, { type => 'error', code => $is_a_modif ? 'error_on_update' : 'error_on_insert' };
+ } else {
+ push @messages, { type => 'message', code => $is_a_modif ? 'success_on_update' : 'success_on_insert' };
}
+
$cache->clear_from_cache("MarcStructure-0-$frameworkcode");
$cache->clear_from_cache("MarcStructure-1-$frameworkcode");
$cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
diff --git a/cataloguing/addbiblio.pl b/cataloguing/addbiblio.pl
index 46f28d19dfb..453c21b484b 100755
--- a/cataloguing/addbiblio.pl
+++ b/cataloguing/addbiblio.pl
@@ -37,7 +37,7 @@ use C4::Biblio qw(
TransformHtmlToMarc
ApplyMarcOverlayRules
);
-use C4::Search qw( FindDuplicate enabled_staff_search_views );
+use C4::Search qw( enabled_staff_search_views );
use C4::Auth qw( get_template_and_user haspermission );
use C4::Context;
use MARC::Record;
@@ -694,14 +694,14 @@ if ( $op eq "cud-addbiblio" ) {
$record = TransformHtmlToMarc( $input, 1 );
# check for a duplicate
- my ( $duplicatebiblionumber, $duplicatetitle );
+ my $duplicate;
if ( !$is_a_modif ) {
- ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($record);
+ ($duplicate) = C4::Search::FindDuplicateWithMatchingRules($record, $frameworkcode);
}
my $confirm_not_duplicate = $input->param('confirm_not_duplicate');
# it is not a duplicate (determined either by Koha itself or by user checking it's not a duplicate)
- if ( !$duplicatebiblionumber or $confirm_not_duplicate ) {
+ if ( !$duplicate or ( !$duplicate->{forbid_duplicate_creation} and $confirm_not_duplicate ) ) {
my $oldbibitemnum;
if ($is_a_modif) {
ModBiblio(
@@ -796,9 +796,7 @@ if ( $op eq "cud-addbiblio" ) {
$template->param(
biblionumber => $biblionumber,
biblioitemnumber => $biblioitemnumber,
- duplicatebiblionumber => $duplicatebiblionumber,
- duplicatebibid => $duplicatebiblionumber,
- duplicatetitle => $duplicatetitle,
+ duplicate => $duplicate,
);
}
diff --git a/installer/data/mysql/atomicupdate/bug-15248.pl b/installer/data/mysql/atomicupdate/bug-15248.pl
new file mode 100644
index 00000000000..3daa0e394e4
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/bug-15248.pl
@@ -0,0 +1,29 @@
+use Modern::Perl;
+
+return {
+ bug_number => "15248",
+ description => "Add biblio_framework_marc_matcher table",
+ up => sub {
+ my ($args) = @_;
+ my ( $dbh, $out ) = @$args{qw(dbh out)};
+
+ unless (TableExists('biblio_framework_marc_matcher')) {
+ $dbh->do(
+ q{
+ CREATE TABLE biblio_framework_marc_matcher (
+ frameworkcode VARCHAR(4) NOT NULL COMMENT 'MARC framework code',
+ marc_matcher_id INT(11) NOT NULL COMMENT 'MARC matcher id',
+ forbid_duplicate_creation TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Do not show an option to create a duplicate record when a duplicate is found',
+ PRIMARY KEY (frameworkcode),
+ CONSTRAINT biblio_framework_marc_matcher_ibfk_1
+ FOREIGN KEY (marc_matcher_id) REFERENCES marc_matchers (matcher_id)
+ ON DELETE CASCADE
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
+ }
+ );
+ say $out "Created new table biblio_framework_marc_matcher";
+ } else {
+ say $out "Table biblio_framework_marc_matcher already exists";
+ }
+ },
+};
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index e391dc68f5d..bd6674bb3f2 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -1131,6 +1131,23 @@ CREATE TABLE `biblio_framework` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
+--
+-- Table structure for table `biblio_framework_marc_matcher`
+--
+
+DROP TABLE IF EXISTS `biblio_framework_marc_matcher`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `biblio_framework_marc_matcher` (
+ `frameworkcode` varchar(4) NOT NULL COMMENT 'MARC framework code',
+ `marc_matcher_id` int(11) NOT NULL COMMENT 'MARC matcher id',
+ `forbid_duplicate_creation` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Do not show an option to create a duplicate record when a duplicate is found',
+ PRIMARY KEY (`frameworkcode`),
+ KEY `biblio_framework_marc_matcher_ibfk_1` (`marc_matcher_id`),
+ CONSTRAINT `biblio_framework_marc_matcher_ibfk_1` FOREIGN KEY (`marc_matcher_id`) REFERENCES `marc_matchers` (`matcher_id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `biblio_metadata`
--
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty_duplicate.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty_duplicate.tt
index 78cb23489db..a3af870a765 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty_duplicate.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty_duplicate.tt
@@ -35,7 +35,7 @@
Duplicate warning
You selected a record from an external source that matches an existing record in your catalog:
- [% duplicatetitle | html %]
duplicate.biblionumber %]"> [% duplicate.title | html %]
+ [% t('If the selected matching rule finds a duplicate and this option is enabled, it will not be possible to create a duplicate. This has no effect if no matching rule is selected.') | html %]
+