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

(-)a/C4/AuthoritiesMarc.pm (-53 / +20 lines)
Lines 1394-1399 sub AddAuthorityTrees{ Link Here
1394
        MARCfrom => $MARCfrom,
1394
        MARCfrom => $MARCfrom,
1395
        [ mergeto => $mergeto, ]
1395
        [ mergeto => $mergeto, ]
1396
        [ MARCto => $MARCto, ]
1396
        [ MARCto => $MARCto, ]
1397
        [ biblionumbers => [ $a, $b, $c ], ]
1397
    });
1398
    });
1398
1399
1399
Merge biblios linked to authority $mergefrom.
1400
Merge biblios linked to authority $mergefrom.
Lines 1401-1406 If $mergeto equals mergefrom, the linked biblio field is updated. Link Here
1401
If $mergeto is different, the biblio field will be linked to $mergeto.
1402
If $mergeto is different, the biblio field will be linked to $mergeto.
1402
If $mergeto is missing, the biblio field is deleted.
1403
If $mergeto is missing, the biblio field is deleted.
1403
1404
1405
Normally all biblio records linked to $mergefrom, will be considered. But
1406
you can pass specific numbers via the biblionumbers parameter.
1407
1404
Note: Although $mergefrom and $mergeto will normally be of the same
1408
Note: Although $mergefrom and $mergeto will normally be of the same
1405
authority type, merge also supports moving to another authority type.
1409
authority type, merge also supports moving to another authority type.
1406
1410
Lines 1408-1421 authority type, merge also supports moving to another authority type. Link Here
1408
1412
1409
sub merge {
1413
sub merge {
1410
    my ( $params ) = @_;
1414
    my ( $params ) = @_;
1411
    my $mergefrom = $params->{mergefrom};
1415
    my $mergefrom = $params->{mergefrom} || return;
1412
    my $MARCfrom = $params->{MARCfrom};
1416
    my $MARCfrom = $params->{MARCfrom};
1413
    my $mergeto = $params->{mergeto};
1417
    my $mergeto = $params->{mergeto};
1414
    my $MARCto = $params->{MARCto};
1418
    my $MARCto = $params->{MARCto};
1419
    my @biblionumbers = $params->{biblionumbers}
1420
        # If we do not have biblionumbers, we get all linked biblios
1421
        ? @{ $params->{biblionumbers} }
1422
        : Koha::Authorities->linked_biblionumbers({ authid => $mergefrom });
1423
    return 0 if !@biblionumbers;
1415
1424
1416
    return 0 unless $mergefrom > 0; # prevent abuse
1425
    my $counteditedbiblio = 0;
1417
    my ($counteditedbiblio,$countunmodifiedbiblio,$counterrors)=(0,0,0);        
1426
    my $dbh = C4::Context->dbh;
1418
    my $dbh=C4::Context->dbh;
1419
    my $authfrom = Koha::Authorities->find($mergefrom);
1427
    my $authfrom = Koha::Authorities->find($mergefrom);
1420
    my $authto = Koha::Authorities->find($mergeto);
1428
    my $authto = Koha::Authorities->find($mergeto);
1421
    my $authtypefrom = $authfrom ? Koha::Authority::Types->find($authfrom->authtypecode) : undef;
1429
    my $authtypefrom = $authfrom ? Koha::Authority::Types->find($authfrom->authtypecode) : undef;
Lines 1429-1464 sub merge { Link Here
1429
    @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $auth_tag_to_report_to && $MARCto && $MARCto->field($auth_tag_to_report_to);
1437
    @record_to = $MARCto->field($auth_tag_to_report_to)->subfields() if $auth_tag_to_report_to && $MARCto && $MARCto->field($auth_tag_to_report_to);
1430
    my @record_from;
1438
    my @record_from;
1431
    @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields() if $auth_tag_to_report_from && $MARCfrom && $MARCfrom->field($auth_tag_to_report_from);
1439
    @record_from = $MARCfrom->field($auth_tag_to_report_from)->subfields() if $auth_tag_to_report_from && $MARCfrom && $MARCfrom->field($auth_tag_to_report_from);
1432
    
1440
1433
    my @reccache;
1434
    # search all biblio tags using this authority.
1435
    #Getting marcbiblios impacted by the change.
1436
    #zebra connection
1437
    my $oConnection=C4::Context->Zconn("biblioserver",0);
1438
    # We used to use XML syntax here, but that no longer works.
1439
    # Thankfully, we don't need it.
1440
    my $query;
1441
    $query= "an=".$mergefrom;
1442
    my $oResult = $oConnection->search(new ZOOM::Query::CCL2RPN( $query, $oConnection ));
1443
    my $count = 0;
1444
    if  ($oResult) {
1445
        $count=$oResult->size();
1446
    }
1447
    my $z=0;
1448
    while ( $z<$count ) {
1449
        my $marcrecordzebra = C4::Search::new_record_from_zebra(
1450
            'biblioserver',
1451
            $oResult->record($z)->raw()
1452
        );
1453
        my ( $biblionumbertagfield, $biblionumbertagsubfield ) = &GetMarcFromKohaField( "biblio.biblionumber", '' );
1454
        my $i = ($biblionumbertagfield < 10)
1455
            ? $marcrecordzebra->field( $biblionumbertagfield )->data
1456
            : $marcrecordzebra->subfield( $biblionumbertagfield, $biblionumbertagsubfield );
1457
        my $marcrecorddb = GetMarcBiblio($i);
1458
        push @reccache, $marcrecorddb;
1459
        $z++;
1460
    }
1461
    $oResult->destroy();
1462
    # Get All candidate Tags for the change 
1441
    # Get All candidate Tags for the change 
1463
    # (This will reduce the search scope in marc records).
1442
    # (This will reduce the search scope in marc records).
1464
    # For a deleted authority record, we scan all auth controlled fields
1443
    # For a deleted authority record, we scan all auth controlled fields
Lines 1479-1485 sub merge { Link Here
1479
    # And we need to add $9 in order not to duplicate
1458
    # And we need to add $9 in order not to duplicate
1480
    $skip_subfields->{9} = 1 if !$overwrite;
1459
    $skip_subfields->{9} = 1 if !$overwrite;
1481
1460
1482
    foreach my $marcrecord(@reccache){
1461
    foreach my $biblionumber ( @biblionumbers ) {
1462
        my $marcrecord = GetMarcBiblio( $biblionumber );
1463
        next if !$marcrecord;
1483
        my $update = 0;
1464
        my $update = 0;
1484
        foreach my $tagfield (@$tags_using_authtype) {
1465
        foreach my $tagfield (@$tags_using_authtype) {
1485
            my $countfrom = 0;    # used in strict mode to remove duplicates
1466
            my $countfrom = 0;    # used in strict mode to remove duplicates
Lines 1523-1547 sub merge { Link Here
1523
                $update = 1;
1504
                $update = 1;
1524
            }
1505
            }
1525
        }
1506
        }
1526
        my ($bibliotag,$bibliosubf) = GetMarcFromKohaField("biblio.biblionumber","") ;
1507
        next if !$update;
1527
        my $biblionumber;
1508
        ModBiblio($marcrecord, $biblionumber, GetFrameworkCode($biblionumber));
1528
        if ($bibliotag<10){
1509
        $counteditedbiblio++;
1529
            $biblionumber=$marcrecord->field($bibliotag)->data;
1530
        }
1531
        else {
1532
            $biblionumber=$marcrecord->subfield($bibliotag,$bibliosubf);
1533
        }
1534
        unless ($biblionumber){
1535
            warn "pas de numéro de notice bibliographique dans : ".$marcrecord->as_formatted;
1536
            next;
1537
        }
1538
        if ($update==1){
1539
            &ModBiblio($marcrecord,$biblionumber,GetFrameworkCode($biblionumber)) ;
1540
            $counteditedbiblio++;
1541
            warn $counteditedbiblio if (($counteditedbiblio % 10) and $ENV{DEBUG});
1542
        }    
1543
    }
1510
    }
1544
    return $counteditedbiblio;  
1511
    return $counteditedbiblio;
1545
}
1512
}
1546
1513
1547
sub _merge_newtag {
1514
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({ authid => $authid1 }); # this triggers a merge call
232
    DelAuthority({ authid => $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