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

(-)a/C4/Biblio.pm (-48 / +39 lines)
Lines 310-316 sub ModBiblio { Link Here
310
310
311
    $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
311
    $frameworkcode = "" if !$frameworkcode || $frameworkcode eq "Default"; # XXX
312
312
313
    _strip_item_fields($record, $frameworkcode);
313
    _strip_item_fields($record);
314
314
315
    # update biblionumber and biblioitemnumber in MARC
315
    # update biblionumber and biblioitemnumber in MARC
316
    # FIXME - this is assuming a 1 to 1 relationship between
316
    # FIXME - this is assuming a 1 to 1 relationship between
Lines 344-350 sub ModBiblio { Link Here
344
344
345
=head2 _strip_item_fields
345
=head2 _strip_item_fields
346
346
347
  _strip_item_fields($record, $frameworkcode)
347
  _strip_item_fields($record)
348
348
349
Utility routine to remove item tags from a
349
Utility routine to remove item tags from a
350
MARC bib.
350
MARC bib.
Lines 353-361 MARC bib. Link Here
353
353
354
sub _strip_item_fields {
354
sub _strip_item_fields {
355
    my $record = shift;
355
    my $record = shift;
356
    my $frameworkcode = shift;
357
    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
356
    # get the items before and append them to the biblio before updating the record, atm we just have the biblio
358
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
357
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
359
358
360
    # delete any item fields from incoming record to avoid
359
    # delete any item fields from incoming record to avoid
361
    # duplication or incorrect data - use AddItem() or ModItem()
360
    # duplication or incorrect data - use AddItem() or ModItem()
Lines 1089-1102 sub GetMarcSubfieldStructure { Link Here
1089
sub GetMarcFromKohaField {
1088
sub GetMarcFromKohaField {
1090
    my ( $kohafield ) = @_;
1089
    my ( $kohafield ) = @_;
1091
    return unless $kohafield;
1090
    return unless $kohafield;
1092
    # The next call uses the Default framework since it is AUTHORITATIVE
1091
1093
    # for all Koha to MARC mappings.
1092
    my $cache     = Koha::Caches->get_instance();
1094
    my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1093
    my $cache_key = "MarcFromKohaField-$kohafield";
1095
    my @retval;
1094
    my $cached    = $cache->get_from_cache($cache_key);
1096
    foreach( @{ $mss->{$kohafield} } ) {
1095
    if ( !defined $cached ) {
1097
        push @retval, $_->{tagfield}, $_->{tagsubfield};
1096
        # The next call uses the Default framework since it is AUTHORITATIVE
1098
    }
1097
        # for all Koha to MARC mappings.
1099
    return wantarray ? @retval : ( @retval ? $retval[0] : undef );
1098
        my $mss = GetMarcSubfieldStructure( '', { unsafe => 1 } ); # Do not change framework
1099
        my @retval;
1100
        foreach( @{ $mss->{$kohafield} } ) {
1101
            push @retval, $_->{tagfield}, $_->{tagsubfield};
1102
        }
1103
        $cached = \@retval;
1104
        $cache->set_in_cache( $cache_key, $cached );
1105
    }
1106
    return wantarray ? @$cached : ( @$cached ? $cached->[0] : undef );
1100
}
1107
}
1101
1108
1102
=head2 GetMarcSubfieldStructureFromKohaField
1109
=head2 GetMarcSubfieldStructureFromKohaField
Lines 2804-2810 override if necessary. Link Here
2804
2811
2805
sub EmbedItemsInMarcBiblio {
2812
sub EmbedItemsInMarcBiblio {
2806
    my ($params) = @_;
2813
    my ($params) = @_;
2807
    my ($marc, $biblionumber, $itemnumbers, $opac, $borcat);
2814
    my ($marc, $biblionumber, $itemnumbers, $use_hidingrules, $borcat);
2808
    $marc = $params->{marc_record};
2815
    $marc = $params->{marc_record};
2809
    if ( !$marc ) {
2816
    if ( !$marc ) {
2810
        carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2817
        carp 'EmbedItemsInMarcBiblio: No MARC record passed';
Lines 2812-2860 sub EmbedItemsInMarcBiblio { Link Here
2812
    }
2819
    }
2813
    $biblionumber = $params->{biblionumber};
2820
    $biblionumber = $params->{biblionumber};
2814
    $itemnumbers = $params->{item_numbers};
2821
    $itemnumbers = $params->{item_numbers};
2815
    $opac = $params->{opac};
2822
    $use_hidingrules = $params->{opac};
2816
    $borcat = $params->{borcat} // q{};
2823
    $borcat = $params->{borcat} // q{};
2817
2824
2818
    $itemnumbers = [] unless defined $itemnumbers;
2825
    $itemnumbers = [] unless defined $itemnumbers;
2819
2826
2820
    my $frameworkcode = GetFrameworkCode($biblionumber);
2827
    _strip_item_fields($marc);
2821
    _strip_item_fields($marc, $frameworkcode);
2822
2828
2823
    # ... and embed the current items
2829
    my $hidingrules = {};
2824
    my $dbh = C4::Context->dbh;
2830
    if ($use_hidingrules && $borcat && !UseHidingRulesWithBorrowerCategory($borcat)) {
2825
    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2831
        # don't hide anything from this borrower category
2826
    $sth->execute($biblionumber);
2832
        $use_hidingrules = 0;
2827
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2833
    }
2828
2834
2829
    my @item_fields; # Array holding the actual MARC data for items to be included.
2835
    my $yaml = $use_hidingrules ? C4::Context->preference('OpacHiddenItems') : '';
2830
    my @items;       # Array holding items which are both in the list (sitenumbers)
2836
    if ( $yaml =~ /\S/ ) {
2831
                     # and on this biblionumber
2837
        $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
2838
        eval {
2839
            $hidingrules = YAML::Load($yaml);
2840
        };
2841
        if ($@) {
2842
            carp "Unable to parse OpacHiddenItems syspref : $@";
2843
        }
2844
    }
2832
2845
2833
    # Flag indicating if there is potential hiding.
2846
    my $item_fields = C4::Items::GetMarcItemFields( $biblionumber, $itemnumbers, $hidingrules );
2834
    my $opachiddenitems = $opac
2835
      && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
2836
2847
2837
    require C4::Items;
2848
    $marc->append_fields(@$item_fields) if ( @$item_fields );
2838
    while ( my ($itemnumber) = $sth->fetchrow_array ) {
2839
        next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
2840
        my $i = $opachiddenitems ? C4::Items::GetItem($itemnumber) : undef;
2841
        push @items, { itemnumber => $itemnumber, item => $i };
2842
    }
2843
    my @items2pass = map { $_->{item} } @items;
2844
    my @hiddenitems =
2845
      $opachiddenitems
2846
      ? C4::Items::GetHiddenItemnumbers({
2847
            items  => \@items2pass,
2848
            borcat => $borcat })
2849
      : ();
2850
    # Convert to a hash for quick searching
2851
    my %hiddenitems = map { $_ => 1 } @hiddenitems;
2852
    foreach my $itemnumber ( map { $_->{itemnumber} } @items ) {
2853
        next if $hiddenitems{$itemnumber};
2854
        my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber );
2855
        push @item_fields, $item_marc->field($itemtag);
2856
    }
2857
    $marc->append_fields(@item_fields);
2858
}
2849
}
2859
2850
2860
=head1 INTERNAL FUNCTIONS
2851
=head1 INTERNAL FUNCTIONS
(-)a/C4/Items.pm (-11 / +99 lines)
Lines 74-79 BEGIN { Link Here
74
	GetHostItemsInfo
74
	GetHostItemsInfo
75
	get_hostitemnumbers_of
75
	get_hostitemnumbers_of
76
        GetHiddenItemnumbers
76
        GetHiddenItemnumbers
77
        UseHidingRulesWithBorrowerCategory
77
        ItemSafeToDelete
78
        ItemSafeToDelete
78
        DelItemCheck
79
        DelItemCheck
79
    MoveItemFromBiblio
80
    MoveItemFromBiblio
Lines 1299-1310 to be excluded Link Here
1299
sub GetHiddenItemnumbers {
1300
sub GetHiddenItemnumbers {
1300
    my $params = shift;
1301
    my $params = shift;
1301
    my $items = $params->{items};
1302
    my $items = $params->{items};
1302
    if (my $exceptions = C4::Context->preference('OpacHiddenItemsExceptions') and $params->{'borcat'}){
1303
1303
        foreach my $except (split(/\|/, $exceptions)){
1304
    if ($params->{'borcat'} && !UseHidingRulesWithBorrowerCategory($params->{'borcat'})) {
1304
            if ($params->{'borcat'} eq $except){
1305
        # Don't hide anything from this borrower category
1305
                return; # we dont hide anything for this borrower category
1306
        return;
1306
            }
1307
        }
1308
    }
1307
    }
1309
    my @resultitems;
1308
    my @resultitems;
1310
1309
Lines 1350-1355 sub GetHiddenItemnumbers { Link Here
1350
    return @resultitems;
1349
    return @resultitems;
1351
}
1350
}
1352
1351
1352
=head2 UseHidingRulesWithBorrowerCategory
1353
1354
    if (UseHidingRulesWithBorrowerCategory($category}) { ... }
1355
1356
Checks if item hiding rules should be used with the given borrower category.
1357
1358
=cut
1359
1360
sub UseHidingRulesWithBorrowerCategory {
1361
    my ($borcat) = @_;
1362
1363
    if ($borcat && (my $exceptions = C4::Context->preference('OpacHiddenItemsExceptions'))) {
1364
        foreach my $except (split(/\|/, $exceptions)) {
1365
            if ($borcat eq $except) {
1366
                return 0;
1367
            }
1368
        }
1369
    }
1370
    return 1;
1371
}
1372
1353
=head1 LIMITED USE FUNCTIONS
1373
=head1 LIMITED USE FUNCTIONS
1354
1374
1355
The following functions, while part of the public API,
1375
The following functions, while part of the public API,
Lines 1419-1424 sub Item2Marc { Link Here
1419
	return $itemmarc;
1439
	return $itemmarc;
1420
}
1440
}
1421
1441
1442
=head2 GetMarcItemFields
1443
1444
  my @marc_fields = GetMarcItemFields($biblionumber);
1445
1446
Returns an array of MARC::Record objects of the items for the biblio.
1447
1448
=cut
1449
1450
sub GetMarcItemFields {
1451
	my ( $biblionumber, $itemnumbers, $hidingrules ) = @_;
1452
1453
    my $item_level_itype = C4::Context->preference('item-level_itypes');
1454
    # This is so much faster than using Koha::Items->search that it makes sense even if it's ugly.
1455
    my $query = 'SELECT * FROM items WHERE biblionumber = ?';
1456
    if (@$itemnumbers) {
1457
        $query .= ' AND itemnumber IN (' . join(',', @$itemnumbers) . ')';
1458
    }
1459
    my $sth = C4::Context->dbh->prepare($query);
1460
    $sth->execute($biblionumber);
1461
    my $items = $sth->fetchall_arrayref({});
1462
    $sth->finish();
1463
    my @item_fields;
1464
    my ($itemtag, $itemsubfield) = GetMarcFromKohaField('items.itemnumber');
1465
1466
    ITEMLOOP: foreach my $item (@$items) {
1467
1468
        # Check hiding rules
1469
        if (defined $hidingrules) {
1470
            foreach my $field (keys %$hidingrules) {
1471
                my $val = $item->{$field};
1472
                $val = '' unless defined $val;
1473
1474
                # If the results matches the values in the hiding rules, skip the item
1475
                if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1476
                    next ITEMLOOP;
1477
                }
1478
            }
1479
        }
1480
1481
        # Set correct item type
1482
        if (!$item_level_itype || !$item->{itype}) {
1483
            warn 'item-level_itypes set but no itemtype set for item (' . $item->{itemnumber} . ')' if (!$item->{itype});
1484
            my $biblioitem = Koha::Biblioitems->find($item->{biblioitemnumber});
1485
            $item->{itype} = $biblioitem->itemtype();
1486
        }
1487
1488
        my $mungeditem = {
1489
            map {
1490
                defined($item->{$_}) && $item->{$_} ne '' ? ("items.$_" => $item->{$_}) : ()
1491
            } keys %{ $item }
1492
        };
1493
        my $itemmarc = TransformKohaToMarc($mungeditem);
1494
1495
        my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1496
        if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1497
            foreach my $field ($itemmarc->field($itemtag)){
1498
                $field->add_subfields(@$unlinked_item_subfields);
1499
            }
1500
        }
1501
1502
        push @item_fields, $itemmarc->field($itemtag);
1503
    }
1504
1505
    return \@item_fields;
1506
}
1507
1508
1422
=head1 PRIVATE FUNCTIONS AND VARIABLES
1509
=head1 PRIVATE FUNCTIONS AND VARIABLES
1423
1510
1424
The following functions are not meant to be called
1511
The following functions are not meant to be called
Lines 1555-1564 sub _do_column_fixes_for_mod { Link Here
1555
        (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1642
        (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1556
        $item->{'withdrawn'} = 0;
1643
        $item->{'withdrawn'} = 0;
1557
    }
1644
    }
1558
    if (exists $item->{location}
1645
    if (exists $item->{'location'}
1559
        and $item->{location} ne 'CART'
1646
        and defined $item->{'location'}
1560
        and $item->{location} ne 'PROC'
1647
        and $item->{'location'} ne 'CART'
1561
        and not $item->{permanent_location}
1648
        and $item->{'location'} ne 'PROC'
1649
        and (not defined $item->{'permanent_location'} or not $item->{'permanent_location'})
1562
    ) {
1650
    ) {
1563
        $item->{'permanent_location'} = $item->{'location'};
1651
        $item->{'permanent_location'} = $item->{'location'};
1564
    }
1652
    }
Lines 2223-2229 sub _SearchItems_build_where_fragment { Link Here
2223
        push @columns, Koha::Database->new()->schema()->resultset('Biblioitem')->result_source->columns;
2311
        push @columns, Koha::Database->new()->schema()->resultset('Biblioitem')->result_source->columns;
2224
        my @operators = qw(= != > < >= <= like);
2312
        my @operators = qw(= != > < >= <= like);
2225
        my $field = $filter->{field};
2313
        my $field = $filter->{field};
2226
        if ( (0 < grep /^$field$/, @columns) or (substr($field, 0, 5) eq 'marc:') ) {
2314
        if ( defined $field and ((0 < grep /^$field$/, @columns) or (substr($field, 0, 5) eq 'marc:')) ) {
2227
            my $op = $filter->{operator};
2315
            my $op = $filter->{operator};
2228
            my $query = $filter->{query};
2316
            my $query = $filter->{query};
2229
2317
(-)a/misc/migration_tools/bulkmarcimport.pl (-1 / +1 lines)
Lines 486-492 RECORD: while ( ) { Link Here
486
            # Work on a clone so that if there are real errors, we can maybe
486
            # Work on a clone so that if there are real errors, we can maybe
487
            # fix them up later.
487
            # fix them up later.
488
			my $clone_record = $record->clone();
488
			my $clone_record = $record->clone();
489
            C4::Biblio::_strip_item_fields($clone_record, '');
489
            C4::Biblio::_strip_item_fields($clone_record);
490
            # This sets the marc fields if there was an error, and also calls
490
            # This sets the marc fields if there was an error, and also calls
491
            # defer_marc_save.
491
            # defer_marc_save.
492
            ModBiblioMarc( $clone_record, $biblionumber, $framework );
492
            ModBiblioMarc( $clone_record, $biblionumber, $framework );
(-)a/t/db_dependent/Items.t (-7 / +3 lines)
Lines 690-696 subtest 'C4::Items::_build_default_values_for_mod_marc' => sub { Link Here
690
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => '9', kohafield => 'items.itemnumber' })->store;
690
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => '9', kohafield => 'items.itemnumber' })->store;
691
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'p', kohafield => 'items.barcode' })->store;
691
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'p', kohafield => 'items.barcode' })->store;
692
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'y', kohafield => 'items.itype' })->store;
692
    Koha::MarcSubfieldStructure->new({ frameworkcode => '', tagfield => '952', tagsubfield => 'y', kohafield => 'items.itype' })->store;
693
    Koha::Caches->get_instance->clear_from_cache( "MarcSubfieldStructure-" );
693
    Koha::Caches->get_instance()->flush_all();
694
694
695
    my $itemtype = $builder->build({ source => 'Itemtype' })->{itemtype};
695
    my $itemtype = $builder->build({ source => 'Itemtype' })->{itemtype};
696
696
Lines 731-739 subtest 'C4::Items::_build_default_values_for_mod_marc' => sub { Link Here
731
    Koha::MarcSubfieldStructures->search({ frameworkcode => '', tagfield => '952', tagsubfield => 'p' })->delete;
731
    Koha::MarcSubfieldStructures->search({ frameworkcode => '', tagfield => '952', tagsubfield => 'p' })->delete;
732
732
733
    # And make sure the caches are cleared
733
    # And make sure the caches are cleared
734
    my $cache = Koha::Caches->get_instance();
734
    Koha::Caches->get_instance()->flush_all();
735
    $cache->clear_from_cache("default_value_for_mod_marc-");
736
    $cache->clear_from_cache("MarcSubfieldStructure-");
737
735
738
    # Update the MARC field with another value
736
    # Update the MARC field with another value
739
    $item_record->delete_fields( $barcode_field );
737
    $item_record->delete_fields( $barcode_field );
Lines 748-755 subtest 'C4::Items::_build_default_values_for_mod_marc' => sub { Link Here
748
    $item = GetItem($item_itemnumber);
746
    $item = GetItem($item_itemnumber);
749
    is ( $item->{barcode}, $a_barcode, 'items.barcode is not mapped anymore, so the DB column has not been updated' );
747
    is ( $item->{barcode}, $a_barcode, 'items.barcode is not mapped anymore, so the DB column has not been updated' );
750
748
751
    $cache->clear_from_cache("default_value_for_mod_marc-");
749
    Koha::Caches->get_instance()->flush_all();
752
    $cache->clear_from_cache( "MarcSubfieldStructure-" );
753
    $schema->storage->txn_rollback;
750
    $schema->storage->txn_rollback;
754
};
751
};
755
752
756
- 

Return to bug 20664