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

(-)a/C4/Biblio.pm (-19 / +30 lines)
Lines 2810-2818 sub EmbedItemsInMarcBiblio { Link Here
2810
        carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2810
        carp 'EmbedItemsInMarcBiblio: No MARC record passed';
2811
        return;
2811
        return;
2812
    }
2812
    }
2813
2814
    $itemnumbers = [] unless defined $itemnumbers;
2813
    $itemnumbers = [] unless defined $itemnumbers;
2815
2814
2815
    # Could this be moved lower down and run only when items
2816
    # exists for improved performance? Probably..
2816
    my $frameworkcode = GetFrameworkCode($biblionumber);
2817
    my $frameworkcode = GetFrameworkCode($biblionumber);
2817
    _strip_item_fields($marc, $frameworkcode);
2818
    _strip_item_fields($marc, $frameworkcode);
2818
2819
Lines 2820-2845 sub EmbedItemsInMarcBiblio { Link Here
2820
    my $dbh = C4::Context->dbh;
2821
    my $dbh = C4::Context->dbh;
2821
    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2822
    my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
2822
    $sth->execute($biblionumber);
2823
    $sth->execute($biblionumber);
2823
    my @item_fields;
2824
    my @all_itemnumbers;
2824
    my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2825
    while ( my ($itemnumber) = $sth->fetchrow_array ) {
2825
    my @items;
2826
        push @all_itemnumbers, $itemnumber;
2827
    }
2828
    # Remove invalid item numbers by intersecting with all valid item numbers
2829
    if(@$itemnumbers) {
2830
        my %h = map { $_ => undef } @{$itemnumbers};
2831
        $itemnumbers = [grep { exists $h{$_} } @all_itemnumbers];
2832
    }
2833
    else {
2834
        $itemnumbers = \@all_itemnumbers;
2835
    }
2836
    # If no item numbers then no point in continuing
2837
    return unless (@{$itemnumbers});
2838
2826
    my $opachiddenitems = $opac
2839
    my $opachiddenitems = $opac
2827
      && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
2840
        && ( C4::Context->preference('OpacHiddenItems') !~ /^\s*$/ );
2828
    require C4::Items;
2841
    require C4::Items;
2829
    while ( my ($itemnumber) = $sth->fetchrow_array ) {
2842
2830
        next if @$itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
2843
    my $items = C4::Items::GetItems($itemnumbers);
2831
        my $i = $opachiddenitems ? C4::Items::GetItem($itemnumber) : undef;
2844
    if ($opachiddenitems) {
2832
        push @items, { itemnumber => $itemnumber, item => $i };
2845
        my %hidden_items = map { $_ => undef } C4::Items::GetHiddenItemnumbers(@{$items});
2833
    }
2846
        # Reduce items to non hidden items
2834
    my @hiddenitems =
2847
        $items = [grep { !(exists $hidden_items{$_->{itemnumber}}) } @{$items}];
2835
      $opachiddenitems
2848
    }
2836
      ? C4::Items::GetHiddenItemnumbers( map { $_->{item} } @items )
2849
2837
      : ();
2850
    my ($itemtag) = GetMarcFromKohaField("items.itemnumber", $frameworkcode);
2838
    # Convert to a hash for quick searching
2851
    my @item_fields;
2839
    my %hiddenitems = map { $_ => 1 } @hiddenitems;
2852
    foreach my $item (@{$items}) {
2840
    foreach my $itemnumber ( map { $_->{itemnumber} } @items ) {
2853
        my $item_marc = C4::Items::GetMarcItem($biblionumber, $item);
2841
        next if $hiddenitems{$itemnumber};
2842
        my $item_marc = C4::Items::GetMarcItem( $biblionumber, $itemnumber );
2843
        push @item_fields, $item_marc->field($itemtag);
2854
        push @item_fields, $item_marc->field($itemtag);
2844
    }
2855
    }
2845
    $marc->append_fields(@item_fields);
2856
    $marc->append_fields(@item_fields);
(-)a/C4/Items.pm (-21 / +47 lines)
Lines 144-177 of C<C4::Items> Link Here
144
144
145
Return item information, for a given itemnumber or barcode.
145
Return item information, for a given itemnumber or barcode.
146
The return value is a hashref mapping item column
146
The return value is a hashref mapping item column
147
names to values.  If C<$serial> is true, include serial publication data.
147
names to values, or undef if item was not found.  If C<$serial> is true, include serial publication data.
148
148
149
=cut
149
=cut
150
150
151
sub GetItem {
151
sub GetItem {
152
    my ($itemnumber,$barcode, $serial) = @_;
152
    my ($itemnumber, $barcode, $serial) = @_;
153
    my $dbh = C4::Context->dbh;
153
    my $items = GetItems($itemnumber ? [$itemnumber] : undef, $barcode ? [$barcode] : undef, $serial);
154
    return @{$items} ? $items->[0] : undef;
155
}
154
156
155
    my $item;
157
=head2 GetItems
156
    if ($itemnumber) {
157
        $item = Koha::Items->find( $itemnumber );
158
    } else {
159
        $item = Koha::Items->find( { barcode => $barcode } );
160
    }
161
158
162
    return unless ( $item );
159
  $item = GetItems($itemnumber,$barcode,$serial);
163
160
164
    my $data = $item->unblessed();
161
Return list of item information, for given itemnumbers or barcodes.
165
    $data->{itype} = $item->effective_itemtype(); # set the correct itype
162
The return value is an arrayref with hashrefs mapping item column
163
names to values, or an empty arrayref if no items were found.
164
If C<$serial> is true, include serial publication data.
166
165
166
=cut
167
168
sub GetItems {
169
    my ($itemnumbers, $barcodes, $serial) = @_;
170
    my $dbh = C4::Context->dbh;
171
    my $items_data = [];
172
173
    # Important: If 'item-level_itypes' preference is not set Koha::Item::effective_itemtype()
174
    # will be equal to $item->{itype} and it is possible to fetch the data directly from items table.
175
    # This will have a much smaller footprint then unblessing the item objects fetched through
176
    # find/search. If more calculated attributes are added in the future, this code will need
177
    # to account for that.
178
    if (C4::Context->preference('item-level_itypes')) {
179
        $items_data = Koha::Items->search_unblessed($itemnumbers ? $itemnumbers : { barcode => $barcodes });
180
    }
181
    else {
182
        my @items = Koha::Items->search(
183
            $itemnumbers ? { itemnumber => { IN => $itemnumbers } } : { barcode => { IN => $barcodes } }
184
        );
185
        foreach my $item (@items) {
186
            my $data = $item->unblessed();
187
            # Set the correct itype
188
            $data->{itype} = $item->effective_itemtype();
189
            push @{$items_data}, $data;
190
        }
191
    }
167
    if ($serial) {
192
    if ($serial) {
168
        my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
193
        foreach my $data (@{$items_data}) {
169
        $ssth->execute( $data->{'itemnumber'} );
194
            my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
170
        ( $data->{'serialseq'}, $data->{'publisheddate'} ) = $ssth->fetchrow_array();
195
            $ssth->execute( $data->{'itemnumber'} );
196
            ( $data->{'serialseq'}, $data->{'publisheddate'} ) = $ssth->fetchrow_array();
197
        }
171
    }
198
    }
172
199
    return $items_data;
173
    return $data;
200
}
174
}    # sub GetItem
175
201
176
=head2 CartToShelf
202
=head2 CartToShelf
177
203
Lines 1481-1493 sub GetMarcItem { Link Here
1481
    # while the other treats the MARC representation as authoritative
1507
    # while the other treats the MARC representation as authoritative
1482
    # under certain circumstances.
1508
    # under certain circumstances.
1483
1509
1484
    my $itemrecord = GetItem($itemnumber);
1510
    my $itemrecord = ref($itemnumber) ? $itemnumber : GetItem($itemnumber);
1485
1511
1486
    # Tack on 'items.' prefix to column names so that C4::Biblio::TransformKohaToMarc will work.
1512
    # Tack on 'items.' prefix to column names so that C4::Biblio::TransformKohaToMarc will work.
1487
    # Also, don't emit a subfield if the underlying field is blank.
1513
    # Also, don't emit a subfield if the underlying field is blank.
1488
1514
1489
    
1515
1490
    return Item2Marc($itemrecord,$biblionumber);
1516
    return Item2Marc($itemrecord, $biblionumber);
1491
1517
1492
}
1518
}
1493
sub Item2Marc {
1519
sub Item2Marc {
(-)a/Koha/Items.pm (+66 lines)
Lines 53-58 sub object_class { Link Here
53
    return 'Koha::Item';
53
    return 'Koha::Item';
54
}
54
}
55
55
56
=head2 search_unblessed
57
58
    $items = Koha::Items->search_unblessed($conditions);
59
60
In scalar context, returns an array reference with hashes containing item data, in list context returns an array.
61
Calling search_unblessed should produce the same result as calling Koha::Items->search and iterating
62
though the result unblessing each item. In cases where you only need the item data using this optimized
63
method is much faster. The arguments accepted are a subset and approximation of those supported by
64
Koha::items->search, with only the "barcode" and "itemnumber" attributes supported as conditions.
65
If called with a non-hash conditions argument a search will be performed as if "itemnumber" was the
66
provided condition. Only one condition is accepted and if more are provided the method will die with
67
an error.
68
69
=cut
70
71
sub search_unblessed {
72
    my ($self, $conditions) = @_;
73
    my $items = [];
74
    my $field_name;
75
    if (ref($conditions) eq 'HASH') {
76
        my %valid_condition_fields = (
77
            'barcode' => undef,
78
            'itemnumber' => undef,
79
        );
80
        my @fields = keys %{$conditions};
81
        if (@fields == 1) {
82
            foreach my $field (@fields) {
83
                die("Invalid condition: \"$field\"") unless exists $valid_condition_fields{$field};
84
            }
85
        }
86
        elsif(@fields > 1) {
87
            die("Multiple conditions are not supported");
88
        }
89
        else {
90
            die("No conditions provided");
91
        }
92
    }
93
    else {
94
        $conditions = {
95
            'itemnumber' => $conditions,
96
        };
97
    }
98
    # Only accepts one condition
99
    my ($field, $values) = (%{$conditions});
100
101
    if (ref($values) eq 'ARRAY' && @{$values} == 1) {
102
        ($values) = @{$values};
103
    }
104
    if (!ref($values)) {
105
        my $item = C4::Context->dbh->selectrow_hashref(qq{SELECT * FROM items WHERE $field = ?}, undef, $values);
106
        push @{$items}, $item;
107
    }
108
    elsif (ref($values) eq 'ARRAY') {
109
        my $in = join ', ', (('?') x @{$values});
110
        my $sth = C4::Context->dbh->prepare(qq{SELECT * FROM items WHERE $field IN($in)});
111
        $sth->execute(@{$values});
112
        while (my $item = $sth->fetchrow_hashref) {
113
            push @{$items}, $item;
114
        }
115
    }
116
    else {
117
        die("Invalid value for field: \"$field\"");
118
    }
119
    return wantarray ? @{$items} : $items;
120
}
121
56
=head1 AUTHOR
122
=head1 AUTHOR
57
123
58
Kyle M Hall <kyle@bywatersolutions.com>
124
Kyle M Hall <kyle@bywatersolutions.com>
(-)a/t/db_dependent/Items.t (-6 / +80 lines)
Lines 44-50 my $location = 'My Location'; Link Here
44
44
45
subtest 'General Add, Get and Del tests' => sub {
45
subtest 'General Add, Get and Del tests' => sub {
46
46
47
    plan tests => 16;
47
    plan tests => 25;
48
49
    my $item_barcode = 123;
48
50
49
    $schema->storage->txn_begin;
51
    $schema->storage->txn_begin;
50
52
Lines 61-88 subtest 'General Add, Get and Del tests' => sub { Link Here
61
    my ($bibnum, $bibitemnum) = get_biblio();
63
    my ($bibnum, $bibitemnum) = get_biblio();
62
64
63
    # Add an item.
65
    # Add an item.
64
    my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $library->{branchcode}, holdingbranch => $library->{branchcode}, location => $location, itype => $itemtype->{itemtype} } , $bibnum);
66
    my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({
67
            homebranch => $library->{branchcode},
68
            holdingbranch => $library->{branchcode},
69
            location => $location,
70
            itype => $itemtype->{itemtype},
71
            barcode => $item_barcode
72
        }, $bibnum
73
    );
65
    cmp_ok($item_bibnum, '==', $bibnum, "New item is linked to correct biblionumber.");
74
    cmp_ok($item_bibnum, '==', $bibnum, "New item is linked to correct biblionumber.");
66
    cmp_ok($item_bibitemnum, '==', $bibitemnum, "New item is linked to correct biblioitemnumber.");
75
    cmp_ok($item_bibitemnum, '==', $bibitemnum, "New item is linked to correct biblioitemnumber.");
67
76
68
    # Get item.
77
    # Get item.
69
    my $getitem = GetItem($itemnumber);
78
    my $getitem = GetItem($itemnumber);
70
    cmp_ok($getitem->{'itemnumber'}, '==', $itemnumber, "Retrieved item has correct itemnumber.");
79
    cmp_ok($getitem->{'itemnumber'}, '==', $itemnumber, "Retrieved item has correct itemnumber.");
80
    cmp_ok($getitem->{'barcode'}, '==', $item_barcode, "Retrieved item has correct barcode.");
71
    cmp_ok($getitem->{'biblioitemnumber'}, '==', $item_bibitemnum, "Retrieved item has correct biblioitemnumber.");
81
    cmp_ok($getitem->{'biblioitemnumber'}, '==', $item_bibitemnum, "Retrieved item has correct biblioitemnumber.");
72
    is( $getitem->{location}, $location, "The location should not have been modified" );
82
    is( $getitem->{location}, $location, "The location should not have been modified" );
73
    is( $getitem->{permanent_location}, $location, "The permanent_location should have been set to the location value" );
83
    is( $getitem->{permanent_location}, $location, "The permanent_location should have been set to the location value" );
74
84
85
    $getitem = GetItem(undef, $item_barcode);
86
    cmp_ok($getitem->{'itemnumber'}, '==', $itemnumber, "Item retrieved by barcode has correct itemnumber.");
87
75
    # Modify item; setting barcode.
88
    # Modify item; setting barcode.
76
    ModItem({ barcode => '987654321' }, $bibnum, $itemnumber);
89
    $item_barcode = '987654321';
90
    ModItem({ barcode => $item_barcode }, $bibnum, $itemnumber);
77
    my $moditem = GetItem($itemnumber);
91
    my $moditem = GetItem($itemnumber);
78
    cmp_ok($moditem->{'barcode'}, '==', '987654321', 'Modified item barcode successfully to: '.$moditem->{'barcode'} . '.');
92
    cmp_ok($moditem->{'barcode'}, '==', $item_barcode, 'Modified item barcode successfully to: ' . $moditem->{'barcode'} . '.');
93
94
	# Update some more properties to get larger coverage
95
	my $item_properties = {
96
		'dateaccessioned' => '2012-12-12',
97
		'datelastborrowed' => '2013-12-12',
98
		'datelastseen' => '2013-12-12',
99
		'notforloan' => '0',
100
		'damaged' => '0',
101
		'itemlost' => '0',
102
		'itemcallnumber' => '1234',
103
		'restricted' => '0',
104
	};
105
106
    # Clone item propreties since ModItem modifies this argument
107
    # causing later tests to fail
108
    ModItem({%{$item_properties}}, $bibnum, $itemnumber);
109
110
	# Search unblessed tests
111
	my $search_conditions = {
112
		'itemnumber' => $itemnumber,
113
	};
114
	my ($item_object) = Koha::Items->search($search_conditions);
115
	my $item_object_data = $item_object->unblessed;
116
	cmp_ok($item_object_data->{'itemnumber'}, '==', $itemnumber, "Item object retrieved by Koha::Items->search using \"itemnumber\" condition has correct itemnumber.");
117
118
	# Intersect with updated properties
119
	my $updated_item_properties = { map { ($_ => $item_object_data->{$_}) } keys %{$item_properties} };
120
121
	is_deeply($updated_item_properties, $item_properties, "Updated item properties have correct values.");
122
123
    my ($item_data) = Koha::Items->search_unblessed($search_conditions);
124
	cmp_ok(
125
		$item_data->{'itemnumber'},
126
		'==',
127
		$itemnumber,
128
		"Item data retrieved by Koha::Items->search_unblessed using \"itemnumber\" condition has correct itemnumber."
129
	);
130
131
    ($item_data) = Koha::Items->search_unblessed({ 'barcode' => $item_barcode });
132
	cmp_ok(
133
		$item_data->{'itemnumber'},
134
		'==',
135
		$itemnumber,
136
		"Item data retrieved by Koha::Items->search_unblessed using \"barcode\" condition has correct itemnumber."
137
	);
138
139
	is_deeply($item_object_data, $item_data, "Item data retrieved by unblessing item object is identical to item data from Koha::Items->search_unblessed.");
79
140
80
    # Delete item.
141
    # Delete item.
81
    DelItem({ biblionumber => $bibnum, itemnumber => $itemnumber });
142
    DelItem({ biblionumber => $bibnum, itemnumber => $itemnumber });
82
    my $getdeleted = GetItem($itemnumber);
143
    my $getdeleted = GetItem($itemnumber);
83
    is($getdeleted->{'itemnumber'}, undef, "Item deleted as expected.");
144
    is($getdeleted->{'itemnumber'}, undef, "Item deleted as expected.");
84
145
85
    ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $library->{branchcode}, holdingbranch => $library->{branchcode}, location => $location, permanent_location => 'my permanent location', itype => $itemtype->{itemtype} } , $bibnum);
146
    ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({
147
            homebranch => $library->{branchcode},
148
            holdingbranch => $library->{branchcode},
149
            location => $location,
150
            permanent_location => 'my permanent location',
151
            itype => $itemtype->{itemtype},
152
            barcode => $item_barcode
153
        },
154
        $bibnum
155
    );
86
    $getitem = GetItem($itemnumber);
156
    $getitem = GetItem($itemnumber);
87
    is( $getitem->{location}, $location, "The location should not have been modified" );
157
    is( $getitem->{location}, $location, "The location should not have been modified" );
88
    is( $getitem->{permanent_location}, 'my permanent location', "The permanent_location should not have modified" );
158
    is( $getitem->{permanent_location}, 'my permanent location', "The permanent_location should not have modified" );
Lines 100-105 subtest 'General Add, Get and Del tests' => sub { Link Here
100
    t::lib::Mocks::mock_preference('item-level_itypes', '1');
170
    t::lib::Mocks::mock_preference('item-level_itypes', '1');
101
    $getitem = GetItem($itemnumber);
171
    $getitem = GetItem($itemnumber);
102
    is( $getitem->{itype}, $itemtype->{itemtype}, "Itemtype set correctly when using item-level_itypes" );
172
    is( $getitem->{itype}, $itemtype->{itemtype}, "Itemtype set correctly when using item-level_itypes" );
173
    # Good to test this since different code for retrieving items is run in GetItems() depending on item-level_itypes preference
174
    is( $getitem->{itemnumber}, $itemnumber, "Item successfully retrieved by itemnumber when using item-level_itypes" );
175
    $getitem = GetItem(undef, $item_barcode);
176
    is( $getitem->{itemnumber}, $itemnumber, "Item successfully retrieved by barcode when using item-level_itypes" );
177
103
    t::lib::Mocks::mock_preference('item-level_itypes', '0');
178
    t::lib::Mocks::mock_preference('item-level_itypes', '0');
104
    $getitem = GetItem($itemnumber);
179
    $getitem = GetItem($itemnumber);
105
    is( $getitem->{itype}, undef, "Itemtype set correctly when not using item-level_itypes" );
180
    is( $getitem->{itype}, undef, "Itemtype set correctly when not using item-level_itypes" );
106
- 

Return to bug 19884