View | Details | Raw Unified | Return to bug 9988
Collapse All | Expand All

(-)a/C4/AuthoritiesMarc.pm (-53 / +20 lines)
Lines 1393-1398 sub AddAuthorityTrees{ Link Here
1393
        MARCfrom => $MARCfrom,
1393
        MARCfrom => $MARCfrom,
1394
        [ mergeto => $mergeto, ]
1394
        [ mergeto => $mergeto, ]
1395
        [ MARCto => $MARCto, ]
1395
        [ MARCto => $MARCto, ]
1396
        [ biblionumbers => [ $a, $b, $c ], ]
1396
    });
1397
    });
1397
1398
1398
Merge biblios linked to authority $mergefrom.
1399
Merge biblios linked to authority $mergefrom.
Lines 1400-1405 If $mergeto equals mergefrom, the linked biblio field is updated. Link Here
1400
If $mergeto is different, the biblio field will be linked to $mergeto.
1401
If $mergeto is different, the biblio field will be linked to $mergeto.
1401
If $mergeto is missing, the biblio field is deleted.
1402
If $mergeto is missing, the biblio field is deleted.
1402
1403
1404
Normally all biblio records linked to $mergefrom, will be considered. But
1405
you can pass specific numbers via the biblionumbers parameter.
1406
1403
Note: Although $mergefrom and $mergeto will normally be of the same
1407
Note: Although $mergefrom and $mergeto will normally be of the same
1404
authority type, merge also supports moving to another authority type.
1408
authority type, merge also supports moving to another authority type.
1405
1409
Lines 1407-1420 authority type, merge also supports moving to another authority type. Link Here
1407
1411
1408
sub merge {
1412
sub merge {
1409
    my ( $params ) = @_;
1413
    my ( $params ) = @_;
1410
    my $mergefrom = $params->{mergefrom};
1414
    my $mergefrom = $params->{mergefrom} || return;
1411
    my $MARCfrom = $params->{MARCfrom};
1415
    my $MARCfrom = $params->{MARCfrom};
1412
    my $mergeto = $params->{mergeto};
1416
    my $mergeto = $params->{mergeto};
1413
    my $MARCto = $params->{MARCto};
1417
    my $MARCto = $params->{MARCto};
1418
    my @biblionumbers = $params->{biblionumbers}
1419
        # If we do not have biblionumbers, we get all linked biblios
1420
        ? @{ $params->{biblionumbers} }
1421
        : Koha::Authorities->linked_biblionumbers({ authid => $mergefrom });
1422
    return 0 if !@biblionumbers;
1414
1423
1415
    return 0 unless $mergefrom > 0; # prevent abuse
1424
    my $counteditedbiblio = 0;
1416
    my ($counteditedbiblio,$countunmodifiedbiblio,$counterrors)=(0,0,0);        
1425
    my $dbh = C4::Context->dbh;
1417
    my $dbh=C4::Context->dbh;
1418
    my $authfrom = Koha::Authorities->find($mergefrom);
1426
    my $authfrom = Koha::Authorities->find($mergefrom);
1419
    my $authto = Koha::Authorities->find($mergeto);
1427
    my $authto = Koha::Authorities->find($mergeto);
1420
    my $authtypefrom = $authfrom ? Koha::Authority::Types->find($authfrom->authtypecode) : undef;
1428
    my $authtypefrom = $authfrom ? Koha::Authority::Types->find($authfrom->authtypecode) : undef;
Lines 1428-1463 sub merge { Link Here
1428
    @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $auth_tag_to_report_to && $MARCto && $MARCto->field($auth_tag_to_report_to);
1436
    @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $auth_tag_to_report_to && $MARCto && $MARCto->field($auth_tag_to_report_to);
1429
    my @record_from;
1437
    my @record_from;
1430
    @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields() if $auth_tag_to_report_from && $MARCfrom && $MARCfrom->field($auth_tag_to_report_from);
1438
    @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields() if $auth_tag_to_report_from && $MARCfrom && $MARCfrom->field($auth_tag_to_report_from);
1431
    
1439
1432
    my @reccache;
1433
    # search all biblio tags using this authority.
1434
    #Getting marcbiblios impacted by the change.
1435
    #zebra connection
1436
    my $oConnection=C4::Context->Zconn("biblioserver",0);
1437
    # We used to use XML syntax here, but that no longer works.
1438
    # Thankfully, we don't need it.
1439
    my $query;
1440
    $query= "an=".$mergefrom;
1441
    my $oResult = $oConnection->search(new ZOOM::Query::CCL2RPN( $query, $oConnection ));
1442
    my $count = 0;
1443
    if  ($oResult) {
1444
        $count=$oResult->size();
1445
    }
1446
    my $z=0;
1447
    while ( $z<$count ) {
1448
        my $marcrecordzebra = C4::Search::new_record_from_zebra(
1449
            'biblioserver',
1450
            $oResult->record($z)->raw()
1451
        );
1452
        my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' );
1453
        my $i = ($biblionumbertagfield < 10)
1454
            ? $marcrecordzebra->field( $biblionumbertagfield )->data
1455
            : $marcrecordzebra->subfield( $biblionumbertagfield, $biblionumbertagsubfield );
1456
        my $marcrecorddb = GetMarcBiblio($i);
1457
        push @reccache, $marcrecorddb;
1458
        $z++;
1459
    }
1460
    $oResult->destroy();
1461
    # Get All candidate Tags for the change 
1440
    # Get All candidate Tags for the change 
1462
    # (This will reduce the search scope in marc records).
1441
    # (This will reduce the search scope in marc records).
1463
    # For a deleted authority record, we scan all auth controlled fields
1442
    # For a deleted authority record, we scan all auth controlled fields
Lines 1478-1484 sub merge { Link Here
1478
    # And we need to add $9 in order not to duplicate
1457
    # And we need to add $9 in order not to duplicate
1479
    $skip_subfields->{9} = 1 if !$overwrite;
1458
    $skip_subfields->{9} = 1 if !$overwrite;
1480
1459
1481
    foreach my $marcrecord(@reccache){
1460
    foreach my $biblionumber ( @biblionumbers ) {
1461
        my $marcrecord = GetMarcBiblio( $biblionumber );
1462
        next if !$marcrecord;
1482
        my $update = 0;
1463
        my $update = 0;
1483
        foreach my $tagfield (@$tags_using_authtype) {
1464
        foreach my $tagfield (@$tags_using_authtype) {
1484
            my $countfrom = 0;    # used in strict mode to remove duplicates
1465
            my $countfrom = 0;    # used in strict mode to remove duplicates
Lines 1522-1546 sub merge { Link Here
1522
                $update = 1;
1503
                $update = 1;
1523
            }
1504
            }
1524
        }
1505
        }
1525
        my ($bibliotag,$bibliosubf) = GetMarcFromKohaField("biblio.biblionumber","") ;
1506
        next if !$update;
1526
        my $biblionumber;
1507
        ModBiblio($marcrecord, $biblionumber, GetFrameworkCode($biblionumber));
1527
        if ($bibliotag<10){
1508
        $counteditedbiblio++;
1528
            $biblionumber=$marcrecord->field($bibliotag)->data;
1529
        }
1530
        else {
1531
            $biblionumber=$marcrecord->subfield($bibliotag,$bibliosubf);
1532
        }
1533
        unless ($biblionumber){
1534
            warn "pas de numéro de notice bibliographique dans : ".$marcrecord->as_formatted;
1535
            next;
1536
        }
1537
        if ($update==1){
1538
            &ModBiblio($marcrecord,$biblionumber,GetFrameworkCode($biblionumber)) ;
1539
            $counteditedbiblio++;
1540
            warn $counteditedbiblio if (($counteditedbiblio % 10) and $ENV{DEBUG});
1541
        }    
1542
    }
1509
    }
1543
    return $counteditedbiblio;  
1510
    return $counteditedbiblio;
1544
}
1511
}
1545
1512
1546
sub _merge_newtag {
1513
sub _merge_newtag {
(-)a/t/db_dependent/Authorities/Merge.t (-38 / +16 lines)
Lines 9-20 use Test::More tests => 5; Link Here
9
use Getopt::Long;
9
use Getopt::Long;
10
use MARC::Record;
10
use MARC::Record;
11
use Test::MockModule;
11
use Test::MockModule;
12
use Test::MockObject;
13
12
14
use t::lib::Mocks;
13
use t::lib::Mocks;
15
use t::lib::TestBuilder;
14
use t::lib::TestBuilder;
16
15
17
use C4::Biblio;
16
use C4::Biblio;
17
use Koha::Authorities;
18
use Koha::Database;
18
use Koha::Database;
19
19
20
BEGIN {
20
BEGIN {
Lines 30-36 my $schema = Koha::Database->new->schema; Link Here
30
$schema->storage->txn_begin;
30
$schema->storage->txn_begin;
31
31
32
# Global variables, mocking and framework modifications
32
# Global variables, mocking and framework modifications
33
our ( @zebrarecords, $index );
33
our @linkedrecords;
34
my $mocks = set_mocks();
34
my $mocks = set_mocks();
35
our ( $authtype1, $authtype2 ) = modify_framework();
35
our ( $authtype1, $authtype2 ) = modify_framework();
36
36
Lines 58-65 subtest 'Test merge A1 to A2 (within same authtype)' => sub { Link Here
58
    my ( $biblionumber2 ) = AddBiblio($biblio2, '');
58
    my ( $biblionumber2 ) = AddBiblio($biblio2, '');
59
59
60
    # Time to merge
60
    # Time to merge
61
    @zebrarecords = ( $biblio1, $biblio2 );
61
    @linkedrecords = ( $biblionumber1, $biblionumber2 );
62
    $index = 0;
63
    my $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid2, MARCfrom => $auth2, mergeto => $authid1, MARCto => $auth1 });
62
    my $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid2, MARCfrom => $auth2, mergeto => $authid1, MARCto => $auth1 });
64
    is( $rv, 1, 'We expect one biblio record (out of two) to be updated' );
63
    is( $rv, 1, 'We expect one biblio record (out of two) to be updated' );
65
64
Lines 102-109 subtest 'Test merge A1 to modified A1, test strict mode' => sub { Link Here
102
    my ( $biblionumber2 ) = AddBiblio( $MARC2, '');
101
    my ( $biblionumber2 ) = AddBiblio( $MARC2, '');
103
102
104
    # Time to merge in loose mode first
103
    # Time to merge in loose mode first
105
    @zebrarecords = ( $MARC1, $MARC2 );
104
    @linkedrecords = ( $biblionumber1, $biblionumber2 );
106
    $index = 0;
107
    t::lib::Mocks::mock_preference('AuthorityMergeMode', 'loose');
105
    t::lib::Mocks::mock_preference('AuthorityMergeMode', 'loose');
108
    my $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1old, mergeto => $authid1, MARCto => $auth1new });
106
    my $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1old, mergeto => $authid1, MARCto => $auth1new });
109
    is( $rv, 2, 'Both records are updated now' );
107
    is( $rv, 2, 'Both records are updated now' );
Lines 123-130 subtest 'Test merge A1 to modified A1, test strict mode' => sub { Link Here
123
    # Merge again in strict mode
121
    # Merge again in strict mode
124
    t::lib::Mocks::mock_preference('AuthorityMergeMode', 'strict');
122
    t::lib::Mocks::mock_preference('AuthorityMergeMode', 'strict');
125
    ModBiblio( $MARC1, $biblionumber1, '' );
123
    ModBiblio( $MARC1, $biblionumber1, '' );
126
    @zebrarecords = ( $MARC1 );
124
    @linkedrecords = ( $biblionumber1 );
127
    $index = 0;
128
    $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1old, mergeto => $authid1, MARCto => $auth1new });
125
    $rv = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1old, mergeto => $authid1, MARCto => $auth1new });
129
    $biblio1 = GetMarcBiblio($biblionumber1);
126
    $biblio1 = GetMarcBiblio($biblionumber1);
130
    is( $biblio1->field(109)->subfield('b'), undef, 'Subfield overwritten in strict mode' );
127
    is( $biblio1->field(109)->subfield('b'), undef, 'Subfield overwritten in strict mode' );
Lines 170-177 subtest 'Test merge A1 to B1 (changing authtype)' => sub { Link Here
170
    my $oldbiblio = C4::Biblio::GetMarcBiblio( $biblionumber );
167
    my $oldbiblio = C4::Biblio::GetMarcBiblio( $biblionumber );
171
168
172
    # Time to merge
169
    # Time to merge
173
    @zebrarecords = ( $marc );
170
    @linkedrecords = ( $biblionumber );
174
    $index = 0;
175
    my $retval = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1, mergeto => $authid2, MARCto => $auth2 });
171
    my $retval = C4::AuthoritiesMarc::merge({ mergefrom => $authid1, MARCfrom => $auth1, mergeto => $authid2, MARCto => $auth2 });
176
    is( $retval, 1, 'We touched only one biblio' );
172
    is( $retval, 1, 'We touched only one biblio' );
177
173
Lines 232-239 subtest 'Merging authorities should handle deletes (BZ 18070)' => sub { Link Here
232
        MARC::Field->new( '609', '', '', a => 'DEL', 9 => "$authid1" ),
228
        MARC::Field->new( '609', '', '', a => 'DEL', 9 => "$authid1" ),
233
    );
229
    );
234
    my ( $biblionumber ) = C4::Biblio::AddBiblio( $bib1, '' );
230
    my ( $biblionumber ) = C4::Biblio::AddBiblio( $bib1, '' );
235
    @zebrarecords = ( $bib1 );
231
    @linkedrecords = ( $biblionumber );
236
    $index = 0;
237
    DelAuthority( $authid1 ); # this triggers a merge call
232
    DelAuthority( $authid1 ); # this triggers a merge call
238
233
239
    # See what happened in the biblio record
234
    # See what happened in the biblio record
Lines 253-293 subtest 'Merging authorities should handle deletes (BZ 18070)' => sub { Link Here
253
    # record and call merge afterwards.
248
    # record and call merge afterwards.
254
    # This mimics deleting an authority and calling merge later in the
249
    # This mimics deleting an authority and calling merge later in the
255
    # merge_authority.pl cron job (when dontmerge is enabled).
250
    # merge_authority.pl cron job (when dontmerge is enabled).
251
    # We use the biblionumbers parameter here and unmock linked_biblionumbers.
256
    C4::Context->dbh->do( "DELETE FROM auth_header WHERE authid=?", undef, $authid1 );
252
    C4::Context->dbh->do( "DELETE FROM auth_header WHERE authid=?", undef, $authid1 );
257
    @zebrarecords = ( $marc1 );
253
    @linkedrecords = ();
258
    $index = 0;
254
    $mocks->{auth_mod}->unmock_all;
259
    merge({ mergefrom => $authid1 });
255
    merge({ mergefrom => $authid1, biblionumbers => [ $biblionumber ] });
260
    # Final check
256
    # Final check
261
    $marc1 = C4::Biblio::GetMarcBiblio( $biblionumber );
257
    $marc1 = C4::Biblio::GetMarcBiblio( $biblionumber );
262
    is( $marc1->field('609'), undef, 'Merge removed the 609 again even after deleting the authority record' );
258
    is( $marc1->field('609'), undef, 'Merge removed the 609 again even after deleting the authority record' );
263
};
259
};
264
260
265
sub set_mocks {
261
sub set_mocks {
266
    # Mock ZOOM objects: They do nothing actually
262
    # After we removed the Zebra code from merge, we only need to mock
267
    # Get new_record_from_zebra to return the records
263
    # linked_biblionumbers here.
268
264
269
    my $mocks;
265
    my $mocks;
270
    $mocks->{context_mod} = Test::MockModule->new( 'C4::Context' );
266
    $mocks->{auth_mod} = Test::MockModule->new( 'Koha::Authorities' );
271
    $mocks->{search_mod} = Test::MockModule->new( 'C4::Search' );
267
    $mocks->{auth_mod}->mock( 'linked_biblionumbers', sub {
272
    $mocks->{zoom_mod} = Test::MockModule->new( 'ZOOM::Query::CCL2RPN', no_auto => 1 );
268
         return @linkedrecords;
273
    $mocks->{conn_obj} = Test::MockObject->new;
274
    $mocks->{zoom_obj} = Test::MockObject->new;
275
    $mocks->{zoom_record_obj} = Test::MockObject->new;
276
277
    $mocks->{context_mod}->mock( 'Zconn', sub { $mocks->{conn_obj}; } );
278
    $mocks->{search_mod}->mock( 'new_record_from_zebra', sub {
279
         return if $index >= @zebrarecords;
280
         return $zebrarecords[ $index++ ];
281
    });
269
    });
282
    $mocks->{zoom_mod}->mock( 'new', sub {} );
283
284
    $mocks->{conn_obj}->mock( 'search', sub { $mocks->{zoom_obj}; } );
285
    $mocks->{zoom_obj}->mock( 'destroy', sub {} );
286
    $mocks->{zoom_obj}->mock( 'record', sub { $mocks->{zoom_record_obj}; } );
287
    $mocks->{zoom_obj}->mock( 'search', sub {} );
288
    $mocks->{zoom_obj}->mock( 'size', sub { @zebrarecords } );
289
    $mocks->{zoom_record_obj}->mock( 'raw', sub {} );
290
291
    return $mocks;
270
    return $mocks;
292
}
271
}
293
272
294
- 

Return to bug 9988