← Index
NYTProf Performance Profile   « line view »
For svc/members/upsert
  Run on Tue Jan 13 11:50:22 2015
Reported on Tue Jan 13 12:09:50 2015

Filename/mnt/catalyst/koha/C4/Items.pm
StatementsExecuted 33 statements in 8.51ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.35ms16.8msC4::Items::::BEGIN@33C4::Items::BEGIN@33
111756µs773µsC4::Items::::BEGIN@21C4::Items::BEGIN@21
11114µs85µsC4::Items::::BEGIN@32C4::Items::BEGIN@32
11111µs11µsC4::Items::::BEGIN@39C4::Items::BEGIN@39
11111µs30µsC4::Items::::BEGIN@34C4::Items::BEGIN@34
11110µs20µsC4::Items::::BEGIN@29C4::Items::BEGIN@29
11110µs12µsC4::Items::::BEGIN@25C4::Items::BEGIN@25
1119µs46µsC4::Items::::BEGIN@24C4::Items::BEGIN@24
1119µs34µsC4::Items::::BEGIN@28C4::Items::BEGIN@28
1118µs259µsC4::Items::::BEGIN@26C4::Items::BEGIN@26
1118µs292µsC4::Items::::BEGIN@27C4::Items::BEGIN@27
1118µs65µsC4::Items::::BEGIN@31C4::Items::BEGIN@31
1118µs46µsC4::Items::::BEGIN@37C4::Items::BEGIN@37
1118µs107µsC4::Items::::BEGIN@30C4::Items::BEGIN@30
0000s0sC4::Items::::AddItemC4::Items::AddItem
0000s0sC4::Items::::AddItemBatchFromMarcC4::Items::AddItemBatchFromMarc
0000s0sC4::Items::::AddItemFromMarcC4::Items::AddItemFromMarc
0000s0sC4::Items::::CartToShelfC4::Items::CartToShelf
0000s0sC4::Items::::CheckItemPreSaveC4::Items::CheckItemPreSave
0000s0sC4::Items::::DelItemC4::Items::DelItem
0000s0sC4::Items::::DelItemCheckC4::Items::DelItemCheck
0000s0sC4::Items::::GetAnalyticsCountC4::Items::GetAnalyticsCount
0000s0sC4::Items::::GetBarcodeFromItemnumberC4::Items::GetBarcodeFromItemnumber
0000s0sC4::Items::::GetHiddenItemnumbersC4::Items::GetHiddenItemnumbers
0000s0sC4::Items::::GetHostItemsInfoC4::Items::GetHostItemsInfo
0000s0sC4::Items::::GetItemC4::Items::GetItem
0000s0sC4::Items::::GetItemHoldsC4::Items::GetItemHolds
0000s0sC4::Items::::GetItemInfosOfC4::Items::GetItemInfosOf
0000s0sC4::Items::::GetItemLocationC4::Items::GetItemLocation
0000s0sC4::Items::::GetItemStatusC4::Items::GetItemStatus
0000s0sC4::Items::::GetItemnumberFromBarcodeC4::Items::GetItemnumberFromBarcode
0000s0sC4::Items::::GetItemnumbersForBiblioC4::Items::GetItemnumbersForBiblio
0000s0sC4::Items::::GetItemsByBiblioitemnumberC4::Items::GetItemsByBiblioitemnumber
0000s0sC4::Items::::GetItemsCountC4::Items::GetItemsCount
0000s0sC4::Items::::GetItemsForInventoryC4::Items::GetItemsForInventory
0000s0sC4::Items::::GetItemsInfoC4::Items::GetItemsInfo
0000s0sC4::Items::::GetItemsLocationInfoC4::Items::GetItemsLocationInfo
0000s0sC4::Items::::GetLastAcquisitionsC4::Items::GetLastAcquisitions
0000s0sC4::Items::::GetLostItemsC4::Items::GetLostItems
0000s0sC4::Items::::GetMarcItemC4::Items::GetMarcItem
0000s0sC4::Items::::Item2MarcC4::Items::Item2Marc
0000s0sC4::Items::::ModDateLastSeenC4::Items::ModDateLastSeen
0000s0sC4::Items::::ModItemC4::Items::ModItem
0000s0sC4::Items::::ModItemFromMarcC4::Items::ModItemFromMarc
0000s0sC4::Items::::ModItemTransferC4::Items::ModItemTransfer
0000s0sC4::Items::::MoveItemFromBiblioC4::Items::MoveItemFromBiblio
0000s0sC4::Items::::PrepareItemrecordDisplayC4::Items::PrepareItemrecordDisplay
0000s0sC4::Items::::SearchItemsC4::Items::SearchItems
0000s0sC4::Items::::ShelfToCartC4::Items::ShelfToCart
0000s0sC4::Items::::__ANON__[:1679]C4::Items::__ANON__[:1679]
0000s0sC4::Items::::_calc_items_cn_sortC4::Items::_calc_items_cn_sort
0000s0sC4::Items::::_do_column_fixes_for_modC4::Items::_do_column_fixes_for_mod
0000s0sC4::Items::::_find_valueC4::Items::_find_value
0000s0sC4::Items::::_get_items_columnsC4::Items::_get_items_columns
0000s0sC4::Items::::_get_single_item_columnC4::Items::_get_single_item_column
0000s0sC4::Items::::_get_unlinked_item_subfieldsC4::Items::_get_unlinked_item_subfields
0000s0sC4::Items::::_get_unlinked_subfields_xmlC4::Items::_get_unlinked_subfields_xml
0000s0sC4::Items::::_koha_delete_itemC4::Items::_koha_delete_item
0000s0sC4::Items::::_koha_modify_itemC4::Items::_koha_modify_item
0000s0sC4::Items::::_koha_new_itemC4::Items::_koha_new_item
0000s0sC4::Items::::_marc_from_item_hashC4::Items::_marc_from_item_hash
0000s0sC4::Items::::_parse_unlinked_item_subfields_from_xmlC4::Items::_parse_unlinked_item_subfields_from_xml
0000s0sC4::Items::::_repack_item_errorsC4::Items::_repack_item_errors
0000s0sC4::Items::::_set_defaults_for_addC4::Items::_set_defaults_for_add
0000s0sC4::Items::::_set_derived_columns_for_addC4::Items::_set_derived_columns_for_add
0000s0sC4::Items::::_set_derived_columns_for_modC4::Items::_set_derived_columns_for_mod
0000s0sC4::Items::::get_authorised_value_imagesC4::Items::get_authorised_value_images
0000s0sC4::Items::::get_hostitemnumbers_ofC4::Items::get_hostitemnumbers_of
0000s0sC4::Items::::get_item_authorised_valuesC4::Items::get_item_authorised_values
0000s0sC4::Items::::get_itemnumbers_ofC4::Items::get_itemnumbers_of
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package C4::Items;
2
3# Copyright 2007 LibLime, Inc.
4# Parts Copyright Biblibre 2010
5#
6# This file is part of Koha.
7#
8# Koha is free software; you can redistribute it and/or modify it under the
9# terms of the GNU General Public License as published by the Free Software
10# Foundation; either version 2 of the License, or (at your option) any later
11# version.
12#
13# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with Koha; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21232µs2791µs
# spent 773µs (756+18) within C4::Items::BEGIN@21 which was called: # once (756µs+18µs) by C4::Reserves::BEGIN@29 at line 21
use strict;
# spent 773µs making 1 call to C4::Items::BEGIN@21 # spent 18µs making 1 call to strict::import
22#use warnings; FIXME - Bug 2505
23
24224µs284µs
# spent 46µs (9+37) within C4::Items::BEGIN@24 which was called: # once (9µs+37µs) by C4::Reserves::BEGIN@29 at line 24
use Carp;
# spent 46µs making 1 call to C4::Items::BEGIN@24 # spent 37µs making 1 call to Exporter::import
25221µs215µs
# spent 12µs (10+2) within C4::Items::BEGIN@25 which was called: # once (10µs+2µs) by C4::Reserves::BEGIN@29 at line 25
use C4::Context;
# spent 12µs making 1 call to C4::Items::BEGIN@25 # spent 2µs making 1 call to C4::Context::import
26225µs2510µs
# spent 259µs (8+251) within C4::Items::BEGIN@26 which was called: # once (8µs+251µs) by C4::Reserves::BEGIN@29 at line 26
use C4::Koha;
# spent 259µs making 1 call to C4::Items::BEGIN@26 # spent 251µs making 1 call to Exporter::import
27232µs2575µs
# spent 292µs (8+284) within C4::Items::BEGIN@27 which was called: # once (8µs+284µs) by C4::Reserves::BEGIN@29 at line 27
use C4::Biblio;
# spent 292µs making 1 call to C4::Items::BEGIN@27 # spent 284µs making 1 call to Exporter::import
28222µs258µs
# spent 34µs (9+25) within C4::Items::BEGIN@28 which was called: # once (9µs+25µs) by C4::Reserves::BEGIN@29 at line 28
use C4::Dates qw/format_date format_date_in_iso/;
# spent 34µs making 1 call to C4::Items::BEGIN@28 # spent 25µs making 1 call to Exporter::import
29222µs230µs
# spent 20µs (10+10) within C4::Items::BEGIN@29 which was called: # once (10µs+10µs) by C4::Reserves::BEGIN@29 at line 29
use MARC::Record;
# spent 20µs making 1 call to C4::Items::BEGIN@29 # spent 10µs making 1 call to Exporter::import
30223µs2206µs
# spent 107µs (8+99) within C4::Items::BEGIN@30 which was called: # once (8µs+99µs) by C4::Reserves::BEGIN@29 at line 30
use C4::ClassSource;
# spent 107µs making 1 call to C4::Items::BEGIN@30 # spent 99µs making 1 call to Exporter::import
31224µs2122µs
# spent 65µs (8+57) within C4::Items::BEGIN@31 which was called: # once (8µs+57µs) by C4::Reserves::BEGIN@29 at line 31
use C4::Log;
# spent 65µs making 1 call to C4::Items::BEGIN@31 # spent 57µs making 1 call to Exporter::import
32230µs2155µs
# spent 85µs (14+71) within C4::Items::BEGIN@32 which was called: # once (14µs+71µs) by C4::Reserves::BEGIN@29 at line 32
use List::MoreUtils qw/any/;
# spent 85µs making 1 call to C4::Items::BEGIN@32 # spent 71µs making 1 call to Exporter::import
332724µs216.8ms
# spent 16.8ms (1.35+15.5) within C4::Items::BEGIN@33 which was called: # once (1.35ms+15.5ms) by C4::Reserves::BEGIN@29 at line 33
use YAML qw/Load/;
# spent 16.8ms making 1 call to C4::Items::BEGIN@33 # spent 33µs making 1 call to Exporter::import
34230µs250µs
# spent 30µs (11+20) within C4::Items::BEGIN@34 which was called: # once (11µs+20µs) by C4::Reserves::BEGIN@29 at line 34
use Data::Dumper; # used as part of logging item record changes, not just for
# spent 30µs making 1 call to C4::Items::BEGIN@34 # spent 20µs making 1 call to Exporter::import
35 # debugging; so please don't remove this
36
37274µs284µs
# spent 46µs (8+38) within C4::Items::BEGIN@37 which was called: # once (8µs+38µs) by C4::Reserves::BEGIN@29 at line 37
use vars qw($VERSION @ISA @EXPORT);
# spent 46µs making 1 call to C4::Items::BEGIN@37 # spent 38µs making 1 call to vars::import
38
39
# spent 11µs within C4::Items::BEGIN@39 which was called: # once (11µs+0s) by C4::Reserves::BEGIN@29 at line 91
BEGIN {
401900ns $VERSION = 3.07.00.049;
41
421500ns require Exporter;
4314µs @ISA = qw( Exporter );
44
45 # function exports
4615µs @EXPORT = qw(
47 GetItem
48 AddItemFromMarc
49 AddItem
50 AddItemBatchFromMarc
51 ModItemFromMarc
52 Item2Marc
53 ModItem
54 ModDateLastSeen
55 ModItemTransfer
56 DelItem
57
58 CheckItemPreSave
59
60 GetItemStatus
61 GetItemLocation
62 GetLostItems
63 GetItemsForInventory
64 GetItemsCount
65 GetItemInfosOf
66 GetItemsByBiblioitemnumber
67 GetItemsInfo
68 GetItemsLocationInfo
69 GetHostItemsInfo
70 GetItemnumbersForBiblio
71 get_itemnumbers_of
72 get_hostitemnumbers_of
73 GetItemnumberFromBarcode
74 GetBarcodeFromItemnumber
75 GetHiddenItemnumbers
76 DelItemCheck
77 MoveItemFromBiblio
78 GetLatestAcquisitions
79
80 CartToShelf
81 ShelfToCart
82
83 GetAnalyticsCount
84 GetItemHolds
85
86 SearchItems
87
88 PrepareItemrecordDisplay
89
90 );
9117.39ms111µs}
# spent 11µs making 1 call to C4::Items::BEGIN@39
92
93=head1 NAME
94
95C4::Items - item management functions
96
97=head1 DESCRIPTION
98
99This module contains an API for manipulating item
100records in Koha, and is used by cataloguing, circulation,
101acquisitions, and serials management.
102
103A Koha item record is stored in two places: the
104items table and embedded in a MARC tag in the XML
105version of the associated bib record in C<biblioitems.marcxml>.
106This is done to allow the item information to be readily
107indexed (e.g., by Zebra), but means that each item
108modification transaction must keep the items table
109and the MARC XML in sync at all times.
110
111Consequently, all code that creates, modifies, or deletes
112item records B<must> use an appropriate function from
113C<C4::Items>. If no existing function is suitable, it is
114better to add one to C<C4::Items> than to use add
115one-off SQL statements to add or modify items.
116
117The items table will be considered authoritative. In other
118words, if there is ever a discrepancy between the items
119table and the MARC XML, the items table should be considered
120accurate.
121
122=head1 HISTORICAL NOTE
123
124Most of the functions in C<C4::Items> were originally in
125the C<C4::Biblio> module.
126
127=head1 CORE EXPORTED FUNCTIONS
128
129The following functions are meant for use by users
130of C<C4::Items>
131
132=cut
133
134=head2 GetItem
135
136 $item = GetItem($itemnumber,$barcode,$serial);
137
138Return item information, for a given itemnumber or barcode.
139The return value is a hashref mapping item column
140names to values. If C<$serial> is true, include serial publication data.
141
142=cut
143
144sub GetItem {
145 my ($itemnumber,$barcode, $serial) = @_;
146 my $dbh = C4::Context->dbh;
147 my $data;
148
149 if ($itemnumber) {
150 my $sth = $dbh->prepare("
151 SELECT * FROM items
152 WHERE itemnumber = ?");
153 $sth->execute($itemnumber);
154 $data = $sth->fetchrow_hashref;
155 } else {
156 my $sth = $dbh->prepare("
157 SELECT * FROM items
158 WHERE barcode = ?"
159 );
160 $sth->execute($barcode);
161 $data = $sth->fetchrow_hashref;
162 }
163
164 return unless ( $data );
165
166 if ( $serial) {
167 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
168 $ssth->execute($data->{'itemnumber'}) ;
169 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
170 }
171 #if we don't have an items.itype, use biblioitems.itemtype.
172 if( ! $data->{'itype'} ) {
173 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
174 $sth->execute($data->{'biblionumber'});
175 ($data->{'itype'}) = $sth->fetchrow_array;
176 }
177 return $data;
178} # sub GetItem
179
180=head2 CartToShelf
181
182 CartToShelf($itemnumber);
183
184Set the current shelving location of the item record
185to its stored permanent shelving location. This is
186primarily used to indicate when an item whose current
187location is a special processing ('PROC') or shelving cart
188('CART') location is back in the stacks.
189
190=cut
191
192sub CartToShelf {
193 my ( $itemnumber ) = @_;
194
195 unless ( $itemnumber ) {
196 croak "FAILED CartToShelf() - no itemnumber supplied";
197 }
198
199 my $item = GetItem($itemnumber);
200 if ( $item->{location} eq 'CART' ) {
201 $item->{location} = $item->{permanent_location};
202 ModItem($item, undef, $itemnumber);
203 }
204}
205
206=head2 ShelfToCart
207
208 ShelfToCart($itemnumber);
209
210Set the current shelving location of the item
211to shelving cart ('CART').
212
213=cut
214
215sub ShelfToCart {
216 my ( $itemnumber ) = @_;
217
218 unless ( $itemnumber ) {
219 croak "FAILED ShelfToCart() - no itemnumber supplied";
220 }
221
222 my $item = GetItem($itemnumber);
223 $item->{'location'} = 'CART';
224 ModItem($item, undef, $itemnumber);
225}
226
227=head2 AddItemFromMarc
228
229 my ($biblionumber, $biblioitemnumber, $itemnumber)
230 = AddItemFromMarc($source_item_marc, $biblionumber);
231
232Given a MARC::Record object containing an embedded item
233record and a biblionumber, create a new item record.
234
235=cut
236
237sub AddItemFromMarc {
238 my ( $source_item_marc, $biblionumber ) = @_;
239 my $dbh = C4::Context->dbh;
240
241 # parse item hash from MARC
242 my $frameworkcode = GetFrameworkCode( $biblionumber );
243 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
244
245 my $localitemmarc=MARC::Record->new;
246 $localitemmarc->append_fields($source_item_marc->field($itemtag));
247 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
248 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
249 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
250}
251
252=head2 AddItem
253
254 my ($biblionumber, $biblioitemnumber, $itemnumber)
255 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
256
257Given a hash containing item column names as keys,
258create a new Koha item record.
259
260The first two optional parameters (C<$dbh> and C<$frameworkcode>)
261do not need to be supplied for general use; they exist
262simply to allow them to be picked up from AddItemFromMarc.
263
264The final optional parameter, C<$unlinked_item_subfields>, contains
265an arrayref containing subfields present in the original MARC
266representation of the item (e.g., from the item editor) that are
267not mapped to C<items> columns directly but should instead
268be stored in C<items.more_subfields_xml> and included in
269the biblio items tag for display and indexing.
270
271=cut
272
273sub AddItem {
274 my $item = shift;
275 my $biblionumber = shift;
276
277 my $dbh = @_ ? shift : C4::Context->dbh;
278 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
279 my $unlinked_item_subfields;
280 if (@_) {
281 $unlinked_item_subfields = shift
282 };
283
284 # needs old biblionumber and biblioitemnumber
285 $item->{'biblionumber'} = $biblionumber;
286 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
287 $sth->execute( $item->{'biblionumber'} );
288 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
289
290 _set_defaults_for_add($item);
291 _set_derived_columns_for_add($item);
292 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
293 # FIXME - checks here
294 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
295 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
296 $itype_sth->execute( $item->{'biblionumber'} );
297 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
298 }
299
300 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
301 $item->{'itemnumber'} = $itemnumber;
302
303 ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver" );
304
305 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
306
307 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
308}
309
310=head2 AddItemBatchFromMarc
311
312 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record,
313 $biblionumber, $biblioitemnumber, $frameworkcode);
314
315Efficiently create item records from a MARC biblio record with
316embedded item fields. This routine is suitable for batch jobs.
317
318This API assumes that the bib record has already been
319saved to the C<biblio> and C<biblioitems> tables. It does
320not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
321are populated, but it will do so via a call to ModBibiloMarc.
322
323The goal of this API is to have a similar effect to using AddBiblio
324and AddItems in succession, but without inefficient repeated
325parsing of the MARC XML bib record.
326
327This function returns an arrayref of new itemsnumbers and an arrayref of item
328errors encountered during the processing. Each entry in the errors
329list is a hashref containing the following keys:
330
331=over
332
333=item item_sequence
334
335Sequence number of original item tag in the MARC record.
336
337=item item_barcode
338
339Item barcode, provide to assist in the construction of
340useful error messages.
341
342=item error_code
343
344Code representing the error condition. Can be 'duplicate_barcode',
345'invalid_homebranch', or 'invalid_holdingbranch'.
346
347=item error_information
348
349Additional information appropriate to the error condition.
350
351=back
352
353=cut
354
355sub AddItemBatchFromMarc {
356 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
357 my $error;
358 my @itemnumbers = ();
359 my @errors = ();
360 my $dbh = C4::Context->dbh;
361
362 # We modify the record, so lets work on a clone so we don't change the
363 # original.
364 $record = $record->clone();
365 # loop through the item tags and start creating items
366 my @bad_item_fields = ();
367 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
368 my $item_sequence_num = 0;
369 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
370 $item_sequence_num++;
371 # we take the item field and stick it into a new
372 # MARC record -- this is required so far because (FIXME)
373 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
374 # and there is no TransformMarcFieldToKoha
375 my $temp_item_marc = MARC::Record->new();
376 $temp_item_marc->append_fields($item_field);
377
378 # add biblionumber and biblioitemnumber
379 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
380 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
381 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
382 $item->{'biblionumber'} = $biblionumber;
383 $item->{'biblioitemnumber'} = $biblioitemnumber;
384
385 # check for duplicate barcode
386 my %item_errors = CheckItemPreSave($item);
387 if (%item_errors) {
388 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
389 push @bad_item_fields, $item_field;
390 next ITEMFIELD;
391 }
392
393 _set_defaults_for_add($item);
394 _set_derived_columns_for_add($item);
395 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
396 warn $error if $error;
397 push @itemnumbers, $itemnumber; # FIXME not checking error
398 $item->{'itemnumber'} = $itemnumber;
399
400 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
401
402 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
403 $item_field->replace_with($new_item_marc->field($itemtag));
404 }
405
406 # remove any MARC item fields for rejected items
407 foreach my $item_field (@bad_item_fields) {
408 $record->delete_field($item_field);
409 }
410
411 # update the MARC biblio
412 # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
413
414 return (\@itemnumbers, \@errors);
415}
416
417=head2 ModItemFromMarc
418
419 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
420
421This function updates an item record based on a supplied
422C<MARC::Record> object containing an embedded item field.
423This API is meant for the use of C<additem.pl>; for
424other purposes, C<ModItem> should be used.
425
426This function uses the hash %default_values_for_mod_from_marc,
427which contains default values for item fields to
428apply when modifying an item. This is needed beccause
429if an item field's value is cleared, TransformMarcToKoha
430does not include the column in the
431hash that's passed to ModItem, which without
432use of this hash makes it impossible to clear
433an item field's value. See bug 2466.
434
435Note that only columns that can be directly
436changed from the cataloging and serials
437item editors are included in this hash.
438
439Returns item record
440
441=cut
442
44319µsmy %default_values_for_mod_from_marc = (
444 barcode => undef,
445 booksellerid => undef,
446 ccode => undef,
447 'items.cn_source' => undef,
448 coded_location_qualifier => undef,
449 copynumber => undef,
450 damaged => 0,
451# dateaccessioned => undef,
452 enumchron => undef,
453 holdingbranch => undef,
454 homebranch => undef,
455 itemcallnumber => undef,
456 itemlost => 0,
457 itemnotes => undef,
458 itype => undef,
459 location => undef,
460 permanent_location => undef,
461 materials => undef,
462 notforloan => 0,
463 paidfor => undef,
464 price => undef,
465 replacementprice => undef,
466 replacementpricedate => undef,
467 restricted => undef,
468 stack => undef,
469 stocknumber => undef,
470 uri => undef,
471 withdrawn => 0,
472);
473
474sub ModItemFromMarc {
475 my $item_marc = shift;
476 my $biblionumber = shift;
477 my $itemnumber = shift;
478
479 my $dbh = C4::Context->dbh;
480 my $frameworkcode = GetFrameworkCode($biblionumber);
481 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
482
483 my $localitemmarc = MARC::Record->new;
484 $localitemmarc->append_fields( $item_marc->field($itemtag) );
485 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
486 foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
487 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
488 }
489 my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
490
491 ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
492 return $item;
493}
494
495=head2 ModItem
496
497 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
498
499Change one or more columns in an item record and update
500the MARC representation of the item.
501
502The first argument is a hashref mapping from item column
503names to the new values. The second and third arguments
504are the biblionumber and itemnumber, respectively.
505
506The fourth, optional parameter, C<$unlinked_item_subfields>, contains
507an arrayref containing subfields present in the original MARC
508representation of the item (e.g., from the item editor) that are
509not mapped to C<items> columns directly but should instead
510be stored in C<items.more_subfields_xml> and included in
511the biblio items tag for display and indexing.
512
513If one of the changed columns is used to calculate
514the derived value of a column such as C<items.cn_sort>,
515this routine will perform the necessary calculation
516and set the value.
517
518=cut
519
520sub ModItem {
521 my $item = shift;
522 my $biblionumber = shift;
523 my $itemnumber = shift;
524
525 # if $biblionumber is undefined, get it from the current item
526 unless (defined $biblionumber) {
527 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
528 }
529
530 my $dbh = @_ ? shift : C4::Context->dbh;
531 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
532
533 my $unlinked_item_subfields;
534 if (@_) {
535 $unlinked_item_subfields = shift;
536 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
537 };
538
539 $item->{'itemnumber'} = $itemnumber or return;
540
541 $item->{onloan} = undef if $item->{itemlost};
542
543 _set_derived_columns_for_mod($item);
544 _do_column_fixes_for_mod($item);
545 # FIXME add checks
546 # duplicate barcode
547 # attempt to change itemnumber
548 # attempt to change biblionumber (if we want
549 # an API to relink an item to a different bib,
550 # it should be a separate function)
551
552 # update items table
553 _koha_modify_item($item);
554
555 # request that bib be reindexed so that searching on current
556 # item status is possible
557 ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
558
559 logaction("CATALOGUING", "MODIFY", $itemnumber, "item ".Dumper($item)) if C4::Context->preference("CataloguingLog");
560}
561
562=head2 ModItemTransfer
563
564 ModItemTransfer($itenumber, $frombranch, $tobranch);
565
566Marks an item as being transferred from one branch
567to another.
568
569=cut
570
571sub ModItemTransfer {
572 my ( $itemnumber, $frombranch, $tobranch ) = @_;
573
574 my $dbh = C4::Context->dbh;
575
576 # Remove the 'shelving cart' location status if it is being used.
577 CartToShelf( $itemnumber ) if ( C4::Context->preference("ReturnToShelvingCart") );
578
579 #new entry in branchtransfers....
580 my $sth = $dbh->prepare(
581 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
582 VALUES (?, ?, NOW(), ?)");
583 $sth->execute($itemnumber, $frombranch, $tobranch);
584
585 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
586 ModDateLastSeen($itemnumber);
587 return;
588}
589
590=head2 ModDateLastSeen
591
592 ModDateLastSeen($itemnum);
593
594Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
595C<$itemnum> is the item number
596
597=cut
598
599sub ModDateLastSeen {
600 my ($itemnumber) = @_;
601
602 my $today = C4::Dates->new();
603 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
604}
605
606=head2 DelItem
607
608 DelItem($dbh, $biblionumber, $itemnumber);
609
610Exported function (core API) for deleting an item record in Koha.
611
612=cut
613
614sub DelItem {
615 my ( $dbh, $biblionumber, $itemnumber ) = @_;
616
617 # FIXME check the item has no current issues
618
619 _koha_delete_item( $dbh, $itemnumber );
620
621 # get the MARC record
622 my $record = GetMarcBiblio($biblionumber);
623 ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
624
625 #search item field code
626 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
627}
628
629=head2 CheckItemPreSave
630
631 my $item_ref = TransformMarcToKoha($marc, 'items');
632 # do stuff
633 my %errors = CheckItemPreSave($item_ref);
634 if (exists $errors{'duplicate_barcode'}) {
635 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
636 } elsif (exists $errors{'invalid_homebranch'}) {
637 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
638 } elsif (exists $errors{'invalid_holdingbranch'}) {
639 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
640 } else {
641 print "item is OK";
642 }
643
644Given a hashref containing item fields, determine if it can be
645inserted or updated in the database. Specifically, checks for
646database integrity issues, and returns a hash containing any
647of the following keys, if applicable.
648
649=over 2
650
651=item duplicate_barcode
652
653Barcode, if it duplicates one already found in the database.
654
655=item invalid_homebranch
656
657Home branch, if not defined in branches table.
658
659=item invalid_holdingbranch
660
661Holding branch, if not defined in branches table.
662
663=back
664
665This function does NOT implement any policy-related checks,
666e.g., whether current operator is allowed to save an
667item that has a given branch code.
668
669=cut
670
671sub CheckItemPreSave {
672 my $item_ref = shift;
673 require C4::Branch;
674
675 my %errors = ();
676
677 # check for duplicate barcode
678 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
679 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
680 if ($existing_itemnumber) {
681 if (!exists $item_ref->{'itemnumber'} # new item
682 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
683 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
684 }
685 }
686 }
687
688 # check for valid home branch
689 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
690 my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'});
691 unless (defined $branch_name) {
692 # relies on fact that branches.branchname is a non-NULL column,
693 # so GetBranchName returns undef only if branch does not exist
694 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
695 }
696 }
697
698 # check for valid holding branch
699 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
700 my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'});
701 unless (defined $branch_name) {
702 # relies on fact that branches.branchname is a non-NULL column,
703 # so GetBranchName returns undef only if branch does not exist
704 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
705 }
706 }
707
708 return %errors;
709
710}
711
712=head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
713
714The following functions provide various ways of
715getting an item record, a set of item records, or
716lists of authorized values for certain item fields.
717
718Some of the functions in this group are candidates
719for refactoring -- for example, some of the code
720in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
721has copy-and-paste work.
722
723=cut
724
725=head2 GetItemStatus
726
727 $itemstatushash = GetItemStatus($fwkcode);
728
729Returns a list of valid values for the
730C<items.notforloan> field.
731
732NOTE: does B<not> return an individual item's
733status.
734
735Can be MARC dependant.
736fwkcode is optional.
737But basically could be can be loan or not
738Create a status selector with the following code
739
740=head3 in PERL SCRIPT
741
742 my $itemstatushash = getitemstatus;
743 my @itemstatusloop;
744 foreach my $thisstatus (keys %$itemstatushash) {
745 my %row =(value => $thisstatus,
746 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
747 );
748 push @itemstatusloop, \%row;
749 }
750 $template->param(statusloop=>\@itemstatusloop);
751
752=head3 in TEMPLATE
753
754<select name="statusloop" id="statusloop">
755 <option value="">Default</option>
756 [% FOREACH statusloo IN statusloop %]
757 [% IF ( statusloo.selected ) %]
758 <option value="[% statusloo.value %]" selected="selected">[% statusloo.statusname %]</option>
759 [% ELSE %]
760 <option value="[% statusloo.value %]">[% statusloo.statusname %]</option>
761 [% END %]
762 [% END %]
763</select>
764
765=cut
766
767sub GetItemStatus {
768
769 # returns a reference to a hash of references to status...
770 my ($fwk) = @_;
771 my %itemstatus;
772 my $dbh = C4::Context->dbh;
773 my $sth;
774 $fwk = '' unless ($fwk);
775 my ( $tag, $subfield ) =
776 GetMarcFromKohaField( "items.notforloan", $fwk );
777 if ( $tag and $subfield ) {
778 my $sth =
779 $dbh->prepare(
780 "SELECT authorised_value
781 FROM marc_subfield_structure
782 WHERE tagfield=?
783 AND tagsubfield=?
784 AND frameworkcode=?
785 "
786 );
787 $sth->execute( $tag, $subfield, $fwk );
788 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
789 my $authvalsth =
790 $dbh->prepare(
791 "SELECT authorised_value,lib
792 FROM authorised_values
793 WHERE category=?
794 ORDER BY lib
795 "
796 );
797 $authvalsth->execute($authorisedvaluecat);
798 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
799 $itemstatus{$authorisedvalue} = $lib;
800 }
801 return \%itemstatus;
802 exit 1;
803 }
804 else {
805
806 #No authvalue list
807 # build default
808 }
809 }
810
811 #No authvalue list
812 #build default
813 $itemstatus{"1"} = "Not For Loan";
814 return \%itemstatus;
815}
816
817=head2 GetItemLocation
818
819 $itemlochash = GetItemLocation($fwk);
820
821Returns a list of valid values for the
822C<items.location> field.
823
824NOTE: does B<not> return an individual item's
825location.
826
827where fwk stands for an optional framework code.
828Create a location selector with the following code
829
830=head3 in PERL SCRIPT
831
832 my $itemlochash = getitemlocation;
833 my @itemlocloop;
834 foreach my $thisloc (keys %$itemlochash) {
835 my $selected = 1 if $thisbranch eq $branch;
836 my %row =(locval => $thisloc,
837 selected => $selected,
838 locname => $itemlochash->{$thisloc},
839 );
840 push @itemlocloop, \%row;
841 }
842 $template->param(itemlocationloop => \@itemlocloop);
843
844=head3 in TEMPLATE
845
846 <select name="location">
847 <option value="">Default</option>
848 <!-- TMPL_LOOP name="itemlocationloop" -->
849 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
850 <!-- /TMPL_LOOP -->
851 </select>
852
853=cut
854
855sub GetItemLocation {
856
857 # returns a reference to a hash of references to location...
858 my ($fwk) = @_;
859 my %itemlocation;
860 my $dbh = C4::Context->dbh;
861 my $sth;
862 $fwk = '' unless ($fwk);
863 my ( $tag, $subfield ) =
864 GetMarcFromKohaField( "items.location", $fwk );
865 if ( $tag and $subfield ) {
866 my $sth =
867 $dbh->prepare(
868 "SELECT authorised_value
869 FROM marc_subfield_structure
870 WHERE tagfield=?
871 AND tagsubfield=?
872 AND frameworkcode=?"
873 );
874 $sth->execute( $tag, $subfield, $fwk );
875 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
876 my $authvalsth =
877 $dbh->prepare(
878 "SELECT authorised_value,lib
879 FROM authorised_values
880 WHERE category=?
881 ORDER BY lib"
882 );
883 $authvalsth->execute($authorisedvaluecat);
884 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
885 $itemlocation{$authorisedvalue} = $lib;
886 }
887 return \%itemlocation;
888 exit 1;
889 }
890 else {
891
892 #No authvalue list
893 # build default
894 }
895 }
896
897 #No authvalue list
898 #build default
899 $itemlocation{"1"} = "Not For Loan";
900 return \%itemlocation;
901}
902
903=head2 GetLostItems
904
905 $items = GetLostItems( $where, $orderby );
906
907This function gets a list of lost items.
908
909=over 2
910
911=item input:
912
913C<$where> is a hashref. it containts a field of the items table as key
914and the value to match as value. For example:
915
916{ barcode => 'abc123',
917 homebranch => 'CPL', }
918
919C<$orderby> is a field of the items table by which the resultset
920should be orderd.
921
922=item return:
923
924C<$items> is a reference to an array full of hashrefs with columns
925from the "items" table as keys.
926
927=item usage in the perl script:
928
929 my $where = { barcode => '0001548' };
930 my $items = GetLostItems( $where, "homebranch" );
931 $template->param( itemsloop => $items );
932
933=back
934
935=cut
936
937sub GetLostItems {
938 # Getting input args.
939 my $where = shift;
940 my $orderby = shift;
941 my $dbh = C4::Context->dbh;
942
943 my $query = "
944 SELECT title, author, lib, itemlost, authorised_value, barcode, datelastseen, price, replacementprice, homebranch,
945 itype, itemtype, holdingbranch, location, itemnotes, items.biblionumber as biblionumber
946 FROM items
947 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
948 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
949 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
950 WHERE
951 authorised_values.category = 'LOST'
952 AND itemlost IS NOT NULL
953 AND itemlost <> 0
954 ";
955 my @query_parameters;
956 foreach my $key (keys %$where) {
957 $query .= " AND $key LIKE ?";
958 push @query_parameters, "%$where->{$key}%";
959 }
960 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
961
962 if ( defined $orderby && grep($orderby, @ordervalues)) {
963 $query .= ' ORDER BY '.$orderby;
964 }
965
966 my $sth = $dbh->prepare($query);
967 $sth->execute( @query_parameters );
968 my $items = [];
969 while ( my $row = $sth->fetchrow_hashref ){
970 push @$items, $row;
971 }
972 return $items;
973}
974
975=head2 GetItemsForInventory
976
977($itemlist, $iTotalRecords) = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $offset, $size, $statushash);
978
979Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
980
981The sub returns a reference to a list of hashes, each containing
982itemnumber, author, title, barcode, item callnumber, and date last
983seen. It is ordered by callnumber then title.
984
985The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
986the datelastseen can be used to specify that you want to see items not seen since a past date only.
987offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
988$statushash requires a hashref that has the authorized values fieldname (intems.notforloan, etc...) as keys, and an arrayref of statuscodes we are searching for as values.
989
990$iTotalRecords is the number of rows that would have been returned without the $offset, $size limit clause
991
992=cut
993
994sub GetItemsForInventory {
995 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
996 my $dbh = C4::Context->dbh;
997 my ( @bind_params, @where_strings );
998
999 my $select_columns = q{
1000 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, biblio.frameworkcode, datelastseen, homebranch, location, notforloan, damaged, itemlost, stocknumber
1001 };
1002 my $select_count = q{SELECT COUNT(*)};
1003 my $query = q{
1004 FROM items
1005 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1006 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1007 };
1008 if ($statushash){
1009 for my $authvfield (keys %$statushash){
1010 if ( scalar @{$statushash->{$authvfield}} > 0 ){
1011 my $joinedvals = join ',', @{$statushash->{$authvfield}};
1012 push @where_strings, "$authvfield in (" . $joinedvals . ")";
1013 }
1014 }
1015 }
1016
1017 if ($minlocation) {
1018 push @where_strings, 'itemcallnumber >= ?';
1019 push @bind_params, $minlocation;
1020 }
1021
1022 if ($maxlocation) {
1023 push @where_strings, 'itemcallnumber <= ?';
1024 push @bind_params, $maxlocation;
1025 }
1026
1027 if ($datelastseen) {
1028 $datelastseen = format_date_in_iso($datelastseen);
1029 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1030 push @bind_params, $datelastseen;
1031 }
1032
1033 if ( $location ) {
1034 push @where_strings, 'items.location = ?';
1035 push @bind_params, $location;
1036 }
1037
1038 if ( $branchcode ) {
1039 if($branch eq "homebranch"){
1040 push @where_strings, 'items.homebranch = ?';
1041 }else{
1042 push @where_strings, 'items.holdingbranch = ?';
1043 }
1044 push @bind_params, $branchcode;
1045 }
1046
1047 if ( $itemtype ) {
1048 push @where_strings, 'biblioitems.itemtype = ?';
1049 push @bind_params, $itemtype;
1050 }
1051
1052 if ( $ignoreissued) {
1053 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1054 push @where_strings, 'issues.date_due IS NULL';
1055 }
1056
1057 if ( @where_strings ) {
1058 $query .= 'WHERE ';
1059 $query .= join ' AND ', @where_strings;
1060 }
1061 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1062 my $count_query = $select_count . $query;
1063 $query .= " LIMIT $offset, $size" if ($offset and $size);
1064 $query = $select_columns . $query;
1065 my $sth = $dbh->prepare($query);
1066 $sth->execute( @bind_params );
1067
1068 my @results = ();
1069 my $tmpresults = $sth->fetchall_arrayref({});
1070 $sth = $dbh->prepare( $count_query );
1071 $sth->execute( @bind_params );
1072 my ($iTotalRecords) = $sth->fetchrow_array();
1073
1074 foreach my $row (@$tmpresults) {
1075
1076 # Auth values
1077 foreach (keys %$row) {
1078 # If the koha field is mapped to a marc field
1079 my ($f, $sf) = GetMarcFromKohaField("items.$_", $row->{'frameworkcode'});
1080 if ($f and $sf) {
1081 # We replace the code with it's description
1082 my $authvals = C4::Koha::GetKohaAuthorisedValuesFromField($f, $sf, $row->{'frameworkcode'});
1083 $row->{$_} = $authvals->{$row->{$_}} if defined $authvals->{$row->{$_}};
1084 }
1085 }
1086 push @results, $row;
1087 }
1088
1089 return (\@results, $iTotalRecords);
1090}
1091
1092=head2 GetItemsCount
1093
1094 $count = &GetItemsCount( $biblionumber);
1095
1096This function return count of item with $biblionumber
1097
1098=cut
1099
1100sub GetItemsCount {
1101 my ( $biblionumber ) = @_;
1102 my $dbh = C4::Context->dbh;
1103 my $query = "SELECT count(*)
1104 FROM items
1105 WHERE biblionumber=?";
1106 my $sth = $dbh->prepare($query);
1107 $sth->execute($biblionumber);
1108 my $count = $sth->fetchrow;
1109 return ($count);
1110}
1111
1112=head2 GetItemInfosOf
1113
1114 GetItemInfosOf(@itemnumbers);
1115
1116=cut
1117
1118sub GetItemInfosOf {
1119 my @itemnumbers = @_;
1120
1121 my $query = '
1122 SELECT *
1123 FROM items
1124 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1125 ';
1126 return get_infos_of( $query, 'itemnumber' );
1127}
1128
1129=head2 GetItemsByBiblioitemnumber
1130
1131 GetItemsByBiblioitemnumber($biblioitemnumber);
1132
1133Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1134Called by C<C4::XISBN>
1135
1136=cut
1137
1138sub GetItemsByBiblioitemnumber {
1139 my ( $bibitem ) = @_;
1140 my $dbh = C4::Context->dbh;
1141 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1142 # Get all items attached to a biblioitem
1143 my $i = 0;
1144 my @results;
1145 $sth->execute($bibitem) || die $sth->errstr;
1146 while ( my $data = $sth->fetchrow_hashref ) {
1147 # Foreach item, get circulation information
1148 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1149 WHERE itemnumber = ?
1150 AND issues.borrowernumber = borrowers.borrowernumber"
1151 );
1152 $sth2->execute( $data->{'itemnumber'} );
1153 if ( my $data2 = $sth2->fetchrow_hashref ) {
1154 # if item is out, set the due date and who it is out too
1155 $data->{'date_due'} = $data2->{'date_due'};
1156 $data->{'cardnumber'} = $data2->{'cardnumber'};
1157 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1158 }
1159 else {
1160 # set date_due to blank, so in the template we check itemlost, and withdrawn
1161 $data->{'date_due'} = '';
1162 } # else
1163 # Find the last 3 people who borrowed this item.
1164 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1165 AND old_issues.borrowernumber = borrowers.borrowernumber
1166 ORDER BY returndate desc,timestamp desc LIMIT 3";
1167 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1168 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1169 my $i2 = 0;
1170 while ( my $data2 = $sth2->fetchrow_hashref ) {
1171 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1172 $data->{"card$i2"} = $data2->{'cardnumber'};
1173 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1174 $i2++;
1175 }
1176 push(@results,$data);
1177 }
1178 return (\@results);
1179}
1180
1181=head2 GetItemsInfo
1182
1183 @results = GetItemsInfo($biblionumber);
1184
1185Returns information about items with the given biblionumber.
1186
1187C<GetItemsInfo> returns a list of references-to-hash. Each element
1188contains a number of keys. Most of them are attributes from the
1189C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1190Koha database. Other keys include:
1191
1192=over 2
1193
1194=item C<$data-E<gt>{branchname}>
1195
1196The name (not the code) of the branch to which the book belongs.
1197
1198=item C<$data-E<gt>{datelastseen}>
1199
1200This is simply C<items.datelastseen>, except that while the date is
1201stored in YYYY-MM-DD format in the database, here it is converted to
1202DD/MM/YYYY format. A NULL date is returned as C<//>.
1203
1204=item C<$data-E<gt>{datedue}>
1205
1206=item C<$data-E<gt>{class}>
1207
1208This is the concatenation of C<biblioitems.classification>, the book's
1209Dewey code, and C<biblioitems.subclass>.
1210
1211=item C<$data-E<gt>{ocount}>
1212
1213I think this is the number of copies of the book available.
1214
1215=item C<$data-E<gt>{order}>
1216
1217If this is set, it is set to C<One Order>.
1218
1219=back
1220
1221=cut
1222
1223sub GetItemsInfo {
1224 my ( $biblionumber ) = @_;
1225 my $dbh = C4::Context->dbh;
1226 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1227 my $query = "
1228 SELECT items.*,
1229 biblio.*,
1230 biblioitems.volume,
1231 biblioitems.number,
1232 biblioitems.itemtype,
1233 biblioitems.isbn,
1234 biblioitems.issn,
1235 biblioitems.publicationyear,
1236 biblioitems.publishercode,
1237 biblioitems.volumedate,
1238 biblioitems.volumedesc,
1239 biblioitems.lccn,
1240 biblioitems.url,
1241 items.notforloan as itemnotforloan,
1242 itemtypes.description,
1243 itemtypes.notforloan as notforloan_per_itemtype,
1244 holding.branchurl,
1245 holding.branchname,
1246 holding.opac_info as branch_opac_info
1247 FROM items
1248 LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1249 LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1250 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1251 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1252 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1253 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1254 $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1255 my $sth = $dbh->prepare($query);
1256 $sth->execute($biblionumber);
1257 my $i = 0;
1258 my @results;
1259 my $serial;
1260
1261 my $isth = $dbh->prepare(
1262 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1263 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1264 WHERE itemnumber = ?"
1265 );
1266 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1267 while ( my $data = $sth->fetchrow_hashref ) {
1268 my $datedue = '';
1269 $isth->execute( $data->{'itemnumber'} );
1270 if ( my $idata = $isth->fetchrow_hashref ) {
1271 $data->{borrowernumber} = $idata->{borrowernumber};
1272 $data->{cardnumber} = $idata->{cardnumber};
1273 $data->{surname} = $idata->{surname};
1274 $data->{firstname} = $idata->{firstname};
1275 $data->{lastreneweddate} = $idata->{lastreneweddate};
1276 $datedue = $idata->{'date_due'};
1277 if (C4::Context->preference("IndependentBranches")){
1278 my $userenv = C4::Context->userenv;
1279 unless ( C4::Context->IsSuperLibrarian() ) {
1280 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1281 }
1282 }
1283 }
1284 if ( $data->{'serial'}) {
1285 $ssth->execute($data->{'itemnumber'}) ;
1286 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1287 $serial = 1;
1288 }
1289 #get branch information.....
1290 my $bsth = $dbh->prepare(
1291 "SELECT * FROM branches WHERE branchcode = ?
1292 "
1293 );
1294 $bsth->execute( $data->{'holdingbranch'} );
1295 if ( my $bdata = $bsth->fetchrow_hashref ) {
1296 $data->{'branchname'} = $bdata->{'branchname'};
1297 }
1298 $data->{'datedue'} = $datedue;
1299
1300 # get notforloan complete status if applicable
1301 if ( my $code = C4::Koha::GetAuthValCode( 'items.notforloan', $data->{frameworkcode} ) ) {
1302 $data->{notforloanvalue} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan} );
1303 $data->{notforloanvalueopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan}, 1 );
1304 }
1305
1306 # get restricted status and description if applicable
1307 if ( my $code = C4::Koha::GetAuthValCode( 'items.restricted', $data->{frameworkcode} ) ) {
1308 $data->{restrictedopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted}, 1 );
1309 $data->{restricted} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted} );
1310 }
1311
1312 # my stack procedures
1313 if ( my $code = C4::Koha::GetAuthValCode( 'items.stack', $data->{frameworkcode} ) ) {
1314 $data->{stack} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{stack} );
1315 }
1316 # Find the last 3 people who borrowed this item.
1317 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1318 WHERE itemnumber = ?
1319 AND old_issues.borrowernumber = borrowers.borrowernumber
1320 ORDER BY returndate DESC
1321 LIMIT 3");
1322 $sth2->execute($data->{'itemnumber'});
1323 my $ii = 0;
1324 while (my $data2 = $sth2->fetchrow_hashref()) {
1325 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1326 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1327 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1328 $ii++;
1329 }
1330
1331 $results[$i] = $data;
1332 $i++;
1333 }
1334 if($serial) {
1335 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1336 } else {
1337 return (@results);
1338 }
1339}
1340
1341=head2 GetItemsLocationInfo
1342
1343 my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1344
1345Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1346
1347C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1348
1349=over 2
1350
1351=item C<$data-E<gt>{homebranch}>
1352
1353Branch Name of the item's homebranch
1354
1355=item C<$data-E<gt>{holdingbranch}>
1356
1357Branch Name of the item's holdingbranch
1358
1359=item C<$data-E<gt>{location}>
1360
1361Item's shelving location code
1362
1363=item C<$data-E<gt>{location_intranet}>
1364
1365The intranet description for the Shelving Location as set in authorised_values 'LOC'
1366
1367=item C<$data-E<gt>{location_opac}>
1368
1369The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC
1370description is set.
1371
1372=item C<$data-E<gt>{itemcallnumber}>
1373
1374Item's itemcallnumber
1375
1376=item C<$data-E<gt>{cn_sort}>
1377
1378Item's call number normalized for sorting
1379
1380=back
1381
1382=cut
1383
1384sub GetItemsLocationInfo {
1385 my $biblionumber = shift;
1386 my @results;
1387
1388 my $dbh = C4::Context->dbh;
1389 my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch,
1390 location, itemcallnumber, cn_sort
1391 FROM items, branches as a, branches as b
1392 WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode
1393 AND biblionumber = ?
1394 ORDER BY cn_sort ASC";
1395 my $sth = $dbh->prepare($query);
1396 $sth->execute($biblionumber);
1397
1398 while ( my $data = $sth->fetchrow_hashref ) {
1399 $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1400 $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1401 push @results, $data;
1402 }
1403 return @results;
1404}
1405
1406=head2 GetHostItemsInfo
1407
1408 $hostiteminfo = GetHostItemsInfo($hostfield);
1409 Returns the iteminfo for items linked to records via a host field
1410
1411=cut
1412
1413sub GetHostItemsInfo {
1414 my ($record) = @_;
1415 my @returnitemsInfo;
1416 unless ($record){
1417 return;
1418 }
1419 if (C4::Context->preference('marcflavour') eq 'MARC21' ||
1420 C4::Context->preference('marcflavour') eq 'NORMARC'){
1421 foreach my $hostfield ( $record->field('773') ) {
1422 my $hostbiblionumber = $hostfield->subfield("0");
1423 my $linkeditemnumber = $hostfield->subfield("9");
1424 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1425 foreach my $hostitemInfo (@hostitemInfos){
1426 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1427 push (@returnitemsInfo,$hostitemInfo);
1428 last;
1429 }
1430 }
1431 }
1432 } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){
1433 foreach my $hostfield ( $record->field('461') ) {
1434 my $hostbiblionumber = $hostfield->subfield("0");
1435 my $linkeditemnumber = $hostfield->subfield("9");
1436 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1437 foreach my $hostitemInfo (@hostitemInfos){
1438 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1439 push (@returnitemsInfo,$hostitemInfo);
1440 last;
1441 }
1442 }
1443 }
1444 }
1445 return @returnitemsInfo;
1446}
1447
1448
1449=head2 GetLastAcquisitions
1450
1451 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'),
1452 'itemtypes' => ('BK','BD')}, 10);
1453
1454=cut
1455
1456sub GetLastAcquisitions {
1457 my ($data,$max) = @_;
1458
1459 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1460
1461 my $number_of_branches = @{$data->{branches}};
1462 my $number_of_itemtypes = @{$data->{itemtypes}};
1463
1464
1465 my @where = ('WHERE 1 ');
1466 $number_of_branches and push @where
1467 , 'AND holdingbranch IN ('
1468 , join(',', ('?') x $number_of_branches )
1469 , ')'
1470 ;
1471
1472 $number_of_itemtypes and push @where
1473 , "AND $itemtype IN ("
1474 , join(',', ('?') x $number_of_itemtypes )
1475 , ')'
1476 ;
1477
1478 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1479 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1480 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1481 @where
1482 GROUP BY biblio.biblionumber
1483 ORDER BY dateaccessioned DESC LIMIT $max";
1484
1485 my $dbh = C4::Context->dbh;
1486 my $sth = $dbh->prepare($query);
1487
1488 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1489
1490 my @results;
1491 while( my $row = $sth->fetchrow_hashref){
1492 push @results, {date => $row->{dateaccessioned}
1493 , biblionumber => $row->{biblionumber}
1494 , title => $row->{title}};
1495 }
1496
1497 return @results;
1498}
1499
1500=head2 GetItemnumbersForBiblio
1501
1502 my $itemnumbers = GetItemnumbersForBiblio($biblionumber);
1503
1504Given a single biblionumber, return an arrayref of all the corresponding itemnumbers
1505
1506=cut
1507
1508sub GetItemnumbersForBiblio {
1509 my $biblionumber = shift;
1510 my @items;
1511 my $dbh = C4::Context->dbh;
1512 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
1513 $sth->execute($biblionumber);
1514 while (my $result = $sth->fetchrow_hashref) {
1515 push @items, $result->{'itemnumber'};
1516 }
1517 return \@items;
1518}
1519
1520=head2 get_itemnumbers_of
1521
1522 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1523
1524Given a list of biblionumbers, return the list of corresponding itemnumbers
1525for each biblionumber.
1526
1527Return a reference on a hash where keys are biblionumbers and values are
1528references on array of itemnumbers.
1529
1530=cut
1531
1532sub get_itemnumbers_of {
1533 my @biblionumbers = @_;
1534
1535 my $dbh = C4::Context->dbh;
1536
1537 my $query = '
1538 SELECT itemnumber,
1539 biblionumber
1540 FROM items
1541 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1542 ';
1543 my $sth = $dbh->prepare($query);
1544 $sth->execute(@biblionumbers);
1545
1546 my %itemnumbers_of;
1547
1548 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1549 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1550 }
1551
1552 return \%itemnumbers_of;
1553}
1554
1555=head2 get_hostitemnumbers_of
1556
1557 my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);
1558
1559Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields
1560
1561Return a reference on a hash where key is a biblionumber and values are
1562references on array of itemnumbers.
1563
1564=cut
1565
1566
1567sub get_hostitemnumbers_of {
1568 my ($biblionumber) = @_;
1569 my $marcrecord = GetMarcBiblio($biblionumber);
1570 my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1571
1572 my $marcflavor = C4::Context->preference('marcflavour');
1573 if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1574 $tag='773';
1575 $biblio_s='0';
1576 $item_s='9';
1577 } elsif ($marcflavor eq 'UNIMARC') {
1578 $tag='461';
1579 $biblio_s='0';
1580 $item_s='9';
1581 }
1582
1583 foreach my $hostfield ( $marcrecord->field($tag) ) {
1584 my $hostbiblionumber = $hostfield->subfield($biblio_s);
1585 my $linkeditemnumber = $hostfield->subfield($item_s);
1586 my @itemnumbers;
1587 if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber})
1588 {
1589 @itemnumbers = @$itemnumbers;
1590 }
1591 foreach my $itemnumber (@itemnumbers){
1592 if ($itemnumber eq $linkeditemnumber){
1593 push (@returnhostitemnumbers,$itemnumber);
1594 last;
1595 }
1596 }
1597 }
1598 return @returnhostitemnumbers;
1599}
1600
1601
1602=head2 GetItemnumberFromBarcode
1603
1604 $result = GetItemnumberFromBarcode($barcode);
1605
1606=cut
1607
1608sub GetItemnumberFromBarcode {
1609 my ($barcode) = @_;
1610 my $dbh = C4::Context->dbh;
1611
1612 my $rq =
1613 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1614 $rq->execute($barcode);
1615 my ($result) = $rq->fetchrow;
1616 return ($result);
1617}
1618
1619=head2 GetBarcodeFromItemnumber
1620
1621 $result = GetBarcodeFromItemnumber($itemnumber);
1622
1623=cut
1624
1625sub GetBarcodeFromItemnumber {
1626 my ($itemnumber) = @_;
1627 my $dbh = C4::Context->dbh;
1628
1629 my $rq =
1630 $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1631 $rq->execute($itemnumber);
1632 my ($result) = $rq->fetchrow;
1633 return ($result);
1634}
1635
1636=head2 GetHiddenItemnumbers
1637
1638 my @itemnumbers_to_hide = GetHiddenItemnumbers(@items);
1639
1640Given a list of items it checks which should be hidden from the OPAC given
1641the current configuration. Returns a list of itemnumbers corresponding to
1642those that should be hidden.
1643
1644=cut
1645
1646sub GetHiddenItemnumbers {
1647 my (@items) = @_;
1648 my @resultitems;
1649
1650 my $yaml = C4::Context->preference('OpacHiddenItems');
1651 return () if (! $yaml =~ /\S/ );
1652 $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1653 my $hidingrules;
1654 eval {
1655 $hidingrules = YAML::Load($yaml);
1656 };
1657 if ($@) {
1658 warn "Unable to parse OpacHiddenItems syspref : $@";
1659 return ();
1660 }
1661 my $dbh = C4::Context->dbh;
1662
1663 # For each item
1664 foreach my $item (@items) {
1665
1666 # We check each rule
1667 foreach my $field (keys %$hidingrules) {
1668 my $val;
1669 if (exists $item->{$field}) {
1670 $val = $item->{$field};
1671 }
1672 else {
1673 my $query = "SELECT $field from items where itemnumber = ?";
1674 $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1675 }
1676 $val = '' unless defined $val;
1677
1678 # If the results matches the values in the yaml file
1679 if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1680
1681 # We add the itemnumber to the list
1682 push @resultitems, $item->{'itemnumber'};
1683
1684 # If at least one rule matched for an item, no need to test the others
1685 last;
1686 }
1687 }
1688 }
1689 return @resultitems;
1690}
1691
1692=head3 get_item_authorised_values
1693
1694find the types and values for all authorised values assigned to this item.
1695
1696parameters: itemnumber
1697
1698returns: a hashref malling the authorised value to the value set for this itemnumber
1699
1700 $authorised_values = {
1701 'CCODE' => undef,
1702 'DAMAGED' => '0',
1703 'LOC' => '3',
1704 'LOST' => '0'
1705 'NOT_LOAN' => '0',
1706 'RESTRICTED' => undef,
1707 'STACK' => undef,
1708 'WITHDRAWN' => '0',
1709 'branches' => 'CPL',
1710 'cn_source' => undef,
1711 'itemtypes' => 'SER',
1712 };
1713
1714Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1715
1716=cut
1717
1718sub get_item_authorised_values {
1719 my $itemnumber = shift;
1720
1721 # assume that these entries in the authorised_value table are item level.
1722 my $query = q(SELECT distinct authorised_value, kohafield
1723 FROM marc_subfield_structure
1724 WHERE kohafield like 'item%'
1725 AND authorised_value != '' );
1726
1727 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1728 my $iteminfo = GetItem( $itemnumber );
1729 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1730 my $return;
1731 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1732 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1733 $field =~ s/^items\.//;
1734 if ( exists $iteminfo->{ $field } ) {
1735 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1736 }
1737 }
1738 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1739 return $return;
1740}
1741
1742=head3 get_authorised_value_images
1743
1744find a list of icons that are appropriate for display based on the
1745authorised values for a biblio.
1746
1747parameters: listref of authorised values, such as comes from
1748get_item_authorised_values or
1749from C4::Biblio::get_biblio_authorised_values
1750
1751returns: listref of hashrefs for each image. Each hashref looks like this:
1752
1753 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1754 label => '',
1755 category => '',
1756 value => '', }
1757
1758Notes: Currently, I put on the full path to the images on the staff
1759side. This should either be configurable or not done at all. Since I
1760have to deal with 'intranet' or 'opac' in
1761get_biblio_authorised_values, perhaps I should be passing it in.
1762
1763=cut
1764
1765sub get_authorised_value_images {
1766 my $authorised_values = shift;
1767
1768 my @imagelist;
1769
1770 my $authorised_value_list = GetAuthorisedValues();
1771 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1772 foreach my $this_authorised_value ( @$authorised_value_list ) {
1773 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1774 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1775 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1776 if ( defined $this_authorised_value->{'imageurl'} ) {
1777 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1778 label => $this_authorised_value->{'lib'},
1779 category => $this_authorised_value->{'category'},
1780 value => $this_authorised_value->{'authorised_value'}, };
1781 }
1782 }
1783 }
1784
1785 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1786 return \@imagelist;
1787
1788}
1789
1790=head1 LIMITED USE FUNCTIONS
1791
1792The following functions, while part of the public API,
1793are not exported. This is generally because they are
1794meant to be used by only one script for a specific
1795purpose, and should not be used in any other context
1796without careful thought.
1797
1798=cut
1799
1800=head2 GetMarcItem
1801
1802 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1803
1804Returns MARC::Record of the item passed in parameter.
1805This function is meant for use only in C<cataloguing/additem.pl>,
1806where it is needed to support that script's MARC-like
1807editor.
1808
1809=cut
1810
1811sub GetMarcItem {
1812 my ( $biblionumber, $itemnumber ) = @_;
1813
1814 # GetMarcItem has been revised so that it does the following:
1815 # 1. Gets the item information from the items table.
1816 # 2. Converts it to a MARC field for storage in the bib record.
1817 #
1818 # The previous behavior was:
1819 # 1. Get the bib record.
1820 # 2. Return the MARC tag corresponding to the item record.
1821 #
1822 # The difference is that one treats the items row as authoritative,
1823 # while the other treats the MARC representation as authoritative
1824 # under certain circumstances.
1825
1826 my $itemrecord = GetItem($itemnumber);
1827
1828 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1829 # Also, don't emit a subfield if the underlying field is blank.
1830
1831
1832 return Item2Marc($itemrecord,$biblionumber);
1833
1834}
1835sub Item2Marc {
1836 my ($itemrecord,$biblionumber)=@_;
1837 my $mungeditem = {
1838 map {
1839 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1840 } keys %{ $itemrecord }
1841 };
1842 my $itemmarc = TransformKohaToMarc($mungeditem);
1843 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1844
1845 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1846 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1847 foreach my $field ($itemmarc->field($itemtag)){
1848 $field->add_subfields(@$unlinked_item_subfields);
1849 }
1850 }
1851 return $itemmarc;
1852}
1853
1854=head1 PRIVATE FUNCTIONS AND VARIABLES
1855
1856The following functions are not meant to be called
1857directly, but are documented in order to explain
1858the inner workings of C<C4::Items>.
1859
1860=cut
1861
1862=head2 %derived_columns
1863
1864This hash keeps track of item columns that
1865are strictly derived from other columns in
1866the item record and are not meant to be set
1867independently.
1868
1869Each key in the hash should be the name of a
1870column (as named by TransformMarcToKoha). Each
1871value should be hashref whose keys are the
1872columns on which the derived column depends. The
1873hashref should also contain a 'BUILDER' key
1874that is a reference to a sub that calculates
1875the derived value.
1876
1877=cut
1878
187912µsmy %derived_columns = (
1880 'items.cn_sort' => {
1881 'itemcallnumber' => 1,
1882 'items.cn_source' => 1,
1883 'BUILDER' => \&_calc_items_cn_sort,
1884 }
1885);
1886
1887=head2 _set_derived_columns_for_add
1888
1889 _set_derived_column_for_add($item);
1890
1891Given an item hash representing a new item to be added,
1892calculate any derived columns. Currently the only
1893such column is C<items.cn_sort>.
1894
1895=cut
1896
1897sub _set_derived_columns_for_add {
1898 my $item = shift;
1899
1900 foreach my $column (keys %derived_columns) {
1901 my $builder = $derived_columns{$column}->{'BUILDER'};
1902 my $source_values = {};
1903 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1904 next if $source_column eq 'BUILDER';
1905 $source_values->{$source_column} = $item->{$source_column};
1906 }
1907 $builder->($item, $source_values);
1908 }
1909}
1910
1911=head2 _set_derived_columns_for_mod
1912
1913 _set_derived_column_for_mod($item);
1914
1915Given an item hash representing a new item to be modified.
1916calculate any derived columns. Currently the only
1917such column is C<items.cn_sort>.
1918
1919This routine differs from C<_set_derived_columns_for_add>
1920in that it needs to handle partial item records. In other
1921words, the caller of C<ModItem> may have supplied only one
1922or two columns to be changed, so this function needs to
1923determine whether any of the columns to be changed affect
1924any of the derived columns. Also, if a derived column
1925depends on more than one column, but the caller is not
1926changing all of then, this routine retrieves the unchanged
1927values from the database in order to ensure a correct
1928calculation.
1929
1930=cut
1931
1932sub _set_derived_columns_for_mod {
1933 my $item = shift;
1934
1935 foreach my $column (keys %derived_columns) {
1936 my $builder = $derived_columns{$column}->{'BUILDER'};
1937 my $source_values = {};
1938 my %missing_sources = ();
1939 my $must_recalc = 0;
1940 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1941 next if $source_column eq 'BUILDER';
1942 if (exists $item->{$source_column}) {
1943 $must_recalc = 1;
1944 $source_values->{$source_column} = $item->{$source_column};
1945 } else {
1946 $missing_sources{$source_column} = 1;
1947 }
1948 }
1949 if ($must_recalc) {
1950 foreach my $source_column (keys %missing_sources) {
1951 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1952 }
1953 $builder->($item, $source_values);
1954 }
1955 }
1956}
1957
1958=head2 _do_column_fixes_for_mod
1959
1960 _do_column_fixes_for_mod($item);
1961
1962Given an item hashref containing one or more
1963columns to modify, fix up certain values.
1964Specifically, set to 0 any passed value
1965of C<notforloan>, C<damaged>, C<itemlost>, or
1966C<withdrawn> that is either undefined or
1967contains the empty string.
1968
1969=cut
1970
1971sub _do_column_fixes_for_mod {
1972 my $item = shift;
1973
1974 if (exists $item->{'notforloan'} and
1975 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1976 $item->{'notforloan'} = 0;
1977 }
1978 if (exists $item->{'damaged'} and
1979 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1980 $item->{'damaged'} = 0;
1981 }
1982 if (exists $item->{'itemlost'} and
1983 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1984 $item->{'itemlost'} = 0;
1985 }
1986 if (exists $item->{'withdrawn'} and
1987 (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1988 $item->{'withdrawn'} = 0;
1989 }
1990 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1991 $item->{'permanent_location'} = $item->{'location'};
1992 }
1993 if (exists $item->{'timestamp'}) {
1994 delete $item->{'timestamp'};
1995 }
1996}
1997
1998=head2 _get_single_item_column
1999
2000 _get_single_item_column($column, $itemnumber);
2001
2002Retrieves the value of a single column from an C<items>
2003row specified by C<$itemnumber>.
2004
2005=cut
2006
2007sub _get_single_item_column {
2008 my $column = shift;
2009 my $itemnumber = shift;
2010
2011 my $dbh = C4::Context->dbh;
2012 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
2013 $sth->execute($itemnumber);
2014 my ($value) = $sth->fetchrow();
2015 return $value;
2016}
2017
2018=head2 _calc_items_cn_sort
2019
2020 _calc_items_cn_sort($item, $source_values);
2021
2022Helper routine to calculate C<items.cn_sort>.
2023
2024=cut
2025
2026sub _calc_items_cn_sort {
2027 my $item = shift;
2028 my $source_values = shift;
2029
2030 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2031}
2032
2033=head2 _set_defaults_for_add
2034
2035 _set_defaults_for_add($item_hash);
2036
2037Given an item hash representing an item to be added, set
2038correct default values for columns whose default value
2039is not handled by the DBMS. This includes the following
2040columns:
2041
2042=over 2
2043
2044=item *
2045
2046C<items.dateaccessioned>
2047
2048=item *
2049
2050C<items.notforloan>
2051
2052=item *
2053
2054C<items.damaged>
2055
2056=item *
2057
2058C<items.itemlost>
2059
2060=item *
2061
2062C<items.withdrawn>
2063
2064=back
2065
2066=cut
2067
2068sub _set_defaults_for_add {
2069 my $item = shift;
2070 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
2071 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost withdrawn));
2072}
2073
2074=head2 _koha_new_item
2075
2076 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
2077
2078Perform the actual insert into the C<items> table.
2079
2080=cut
2081
2082sub _koha_new_item {
2083 my ( $item, $barcode ) = @_;
2084 my $dbh=C4::Context->dbh;
2085 my $error;
2086 my $query =
2087 "INSERT INTO items SET
2088 biblionumber = ?,
2089 biblioitemnumber = ?,
2090 barcode = ?,
2091 dateaccessioned = ?,
2092 booksellerid = ?,
2093 homebranch = ?,
2094 price = ?,
2095 replacementprice = ?,
2096 replacementpricedate = ?,
2097 datelastborrowed = ?,
2098 datelastseen = ?,
2099 stack = ?,
2100 notforloan = ?,
2101 damaged = ?,
2102 itemlost = ?,
2103 withdrawn = ?,
2104 itemcallnumber = ?,
2105 coded_location_qualifier = ?,
2106 restricted = ?,
2107 itemnotes = ?,
2108 holdingbranch = ?,
2109 paidfor = ?,
2110 location = ?,
2111 permanent_location = ?,
2112 onloan = ?,
2113 issues = ?,
2114 renewals = ?,
2115 reserves = ?,
2116 cn_source = ?,
2117 cn_sort = ?,
2118 ccode = ?,
2119 itype = ?,
2120 materials = ?,
2121 uri = ?,
2122 enumchron = ?,
2123 more_subfields_xml = ?,
2124 copynumber = ?,
2125 stocknumber = ?
2126 ";
2127 my $sth = $dbh->prepare($query);
2128 my $today = C4::Dates->today('iso');
2129 $sth->execute(
2130 $item->{'biblionumber'},
2131 $item->{'biblioitemnumber'},
2132 $barcode,
2133 $item->{'dateaccessioned'},
2134 $item->{'booksellerid'},
2135 $item->{'homebranch'},
2136 $item->{'price'},
2137 $item->{'replacementprice'},
2138 $item->{'replacementpricedate'} || $today,
2139 $item->{datelastborrowed},
2140 $item->{datelastseen} || $today,
2141 $item->{stack},
2142 $item->{'notforloan'},
2143 $item->{'damaged'},
2144 $item->{'itemlost'},
2145 $item->{'withdrawn'},
2146 $item->{'itemcallnumber'},
2147 $item->{'coded_location_qualifier'},
2148 $item->{'restricted'},
2149 $item->{'itemnotes'},
2150 $item->{'holdingbranch'},
2151 $item->{'paidfor'},
2152 $item->{'location'},
2153 $item->{'permanent_location'},
2154 $item->{'onloan'},
2155 $item->{'issues'},
2156 $item->{'renewals'},
2157 $item->{'reserves'},
2158 $item->{'items.cn_source'},
2159 $item->{'items.cn_sort'},
2160 $item->{'ccode'},
2161 $item->{'itype'},
2162 $item->{'materials'},
2163 $item->{'uri'},
2164 $item->{'enumchron'},
2165 $item->{'more_subfields_xml'},
2166 $item->{'copynumber'},
2167 $item->{'stocknumber'},
2168 );
2169
2170 my $itemnumber;
2171 if ( defined $sth->errstr ) {
2172 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2173 }
2174 else {
2175 $itemnumber = $dbh->{'mysql_insertid'};
2176 }
2177
2178 return ( $itemnumber, $error );
2179}
2180
2181=head2 MoveItemFromBiblio
2182
2183 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2184
2185Moves an item from a biblio to another
2186
2187Returns undef if the move failed or the biblionumber of the destination record otherwise
2188
2189=cut
2190
2191sub MoveItemFromBiblio {
2192 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2193 my $dbh = C4::Context->dbh;
2194 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2195 $sth->execute( $tobiblio );
2196 my ( $tobiblioitem ) = $sth->fetchrow();
2197 $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2198 my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2199 if ($return == 1) {
2200 ModZebra( $tobiblio, "specialUpdate", "biblioserver" );
2201 ModZebra( $frombiblio, "specialUpdate", "biblioserver" );
2202 # Checking if the item we want to move is in an order
2203 require C4::Acquisition;
2204 my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
2205 if ($order) {
2206 # Replacing the biblionumber within the order if necessary
2207 $order->{'biblionumber'} = $tobiblio;
2208 C4::Acquisition::ModOrder($order);
2209 }
2210 return $tobiblio;
2211 }
2212 return;
2213}
2214
2215=head2 DelItemCheck
2216
2217 DelItemCheck($dbh, $biblionumber, $itemnumber);
2218
2219Exported function (core API) for deleting an item record in Koha if there no current issue.
2220
2221=cut
2222
2223sub DelItemCheck {
2224 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2225 my $error;
2226
2227 my $countanalytics=GetAnalyticsCount($itemnumber);
2228
2229
2230 # check that there is no issue on this item before deletion.
2231 my $sth = $dbh->prepare(q{
2232 SELECT COUNT(*) FROM issues
2233 WHERE itemnumber = ?
2234 });
2235 $sth->execute($itemnumber);
2236 my ($onloan) = $sth->fetchrow;
2237
2238 my $item = GetItem($itemnumber);
2239
2240 if ($onloan){
2241 $error = "book_on_loan"
2242 }
2243 elsif ( !C4::Context->IsSuperLibrarian()
2244 and C4::Context->preference("IndependentBranches")
2245 and ( C4::Context->userenv->{branch} ne $item->{'homebranch'} ) )
2246 {
2247 $error = "not_same_branch";
2248 }
2249 else{
2250 # check it doesnt have a waiting reserve
2251 $sth = $dbh->prepare(q{
2252 SELECT COUNT(*) FROM reserves
2253 WHERE (found = 'W' OR found = 'T')
2254 AND itemnumber = ?
2255 });
2256 $sth->execute($itemnumber);
2257 my ($reserve) = $sth->fetchrow;
2258 if ($reserve){
2259 $error = "book_reserved";
2260 } elsif ($countanalytics > 0){
2261 $error = "linked_analytics";
2262 } else {
2263 DelItem($dbh, $biblionumber, $itemnumber);
2264 return 1;
2265 }
2266 }
2267 return $error;
2268}
2269
2270=head2 _koha_modify_item
2271
2272 my ($itemnumber,$error) =_koha_modify_item( $item );
2273
2274Perform the actual update of the C<items> row. Note that this
2275routine accepts a hashref specifying the columns to update.
2276
2277=cut
2278
2279sub _koha_modify_item {
2280 my ( $item ) = @_;
2281 my $dbh=C4::Context->dbh;
2282 my $error;
2283
2284 my $query = "UPDATE items SET ";
2285 my @bind;
2286 for my $key ( keys %$item ) {
2287 next if ( $key eq 'itemnumber' );
2288 $query.="$key=?,";
2289 push @bind, $item->{$key};
2290 }
2291 $query =~ s/,$//;
2292 $query .= " WHERE itemnumber=?";
2293 push @bind, $item->{'itemnumber'};
2294 my $sth = $dbh->prepare($query);
2295 $sth->execute(@bind);
2296 if ( $sth->err ) {
2297 $error.="ERROR in _koha_modify_item $query: ".$sth->errstr;
2298 warn $error;
2299 }
2300 return ($item->{'itemnumber'},$error);
2301}
2302
2303=head2 _koha_delete_item
2304
2305 _koha_delete_item( $dbh, $itemnum );
2306
2307Internal function to delete an item record from the koha tables
2308
2309=cut
2310
2311sub _koha_delete_item {
2312 my ( $dbh, $itemnum ) = @_;
2313
2314 # save the deleted item to deleteditems table
2315 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2316 $sth->execute($itemnum);
2317 my $data = $sth->fetchrow_hashref();
2318 my $query = "INSERT INTO deleteditems SET ";
2319 my @bind = ();
2320 foreach my $key ( keys %$data ) {
2321 next if ( $key eq 'timestamp' ); # timestamp will be set by db
2322 $query .= "$key = ?,";
2323 push( @bind, $data->{$key} );
2324 }
2325 $query =~ s/\,$//;
2326 $sth = $dbh->prepare($query);
2327 $sth->execute(@bind);
2328
2329 # delete from items table
2330 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2331 $sth->execute($itemnum);
2332 return;
2333}
2334
2335=head2 _marc_from_item_hash
2336
2337 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2338
2339Given an item hash representing a complete item record,
2340create a C<MARC::Record> object containing an embedded
2341tag representing that item.
2342
2343The third, optional parameter C<$unlinked_item_subfields> is
2344an arrayref of subfields (not mapped to C<items> fields per the
2345framework) to be added to the MARC representation
2346of the item.
2347
2348=cut
2349
2350sub _marc_from_item_hash {
2351 my $item = shift;
2352 my $frameworkcode = shift;
2353 my $unlinked_item_subfields;
2354 if (@_) {
2355 $unlinked_item_subfields = shift;
2356 }
2357
2358 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2359 # Also, don't emit a subfield if the underlying field is blank.
2360 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2361 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2362 : () } keys %{ $item } };
2363
2364 my $item_marc = MARC::Record->new();
2365 foreach my $item_field ( keys %{$mungeditem} ) {
2366 my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2367 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2368 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2369 foreach my $value (@values){
2370 if ( my $field = $item_marc->field($tag) ) {
2371 $field->add_subfields( $subfield => $value );
2372 } else {
2373 my $add_subfields = [];
2374 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2375 $add_subfields = $unlinked_item_subfields;
2376 }
2377 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2378 }
2379 }
2380 }
2381
2382 return $item_marc;
2383}
2384
2385=head2 _repack_item_errors
2386
2387Add an error message hash generated by C<CheckItemPreSave>
2388to a list of errors.
2389
2390=cut
2391
2392sub _repack_item_errors {
2393 my $item_sequence_num = shift;
2394 my $item_ref = shift;
2395 my $error_ref = shift;
2396
2397 my @repacked_errors = ();
2398
2399 foreach my $error_code (sort keys %{ $error_ref }) {
2400 my $repacked_error = {};
2401 $repacked_error->{'item_sequence'} = $item_sequence_num;
2402 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2403 $repacked_error->{'error_code'} = $error_code;
2404 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2405 push @repacked_errors, $repacked_error;
2406 }
2407
2408 return @repacked_errors;
2409}
2410
2411=head2 _get_unlinked_item_subfields
2412
2413 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2414
2415=cut
2416
2417sub _get_unlinked_item_subfields {
2418 my $original_item_marc = shift;
2419 my $frameworkcode = shift;
2420
2421 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2422
2423 # assume that this record has only one field, and that that
2424 # field contains only the item information
2425 my $subfields = [];
2426 my @fields = $original_item_marc->fields();
2427 if ($#fields > -1) {
2428 my $field = $fields[0];
2429 my $tag = $field->tag();
2430 foreach my $subfield ($field->subfields()) {
2431 if (defined $subfield->[1] and
2432 $subfield->[1] ne '' and
2433 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2434 push @$subfields, $subfield->[0] => $subfield->[1];
2435 }
2436 }
2437 }
2438 return $subfields;
2439}
2440
2441=head2 _get_unlinked_subfields_xml
2442
2443 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2444
2445=cut
2446
2447sub _get_unlinked_subfields_xml {
2448 my $unlinked_item_subfields = shift;
2449
2450 my $xml;
2451 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2452 my $marc = MARC::Record->new();
2453 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2454 # used in the framework
2455 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2456 $marc->encoding("UTF-8");
2457 $xml = $marc->as_xml("USMARC");
2458 }
2459
2460 return $xml;
2461}
2462
2463=head2 _parse_unlinked_item_subfields_from_xml
2464
2465 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2466
2467=cut
2468
2469sub _parse_unlinked_item_subfields_from_xml {
2470 my $xml = shift;
2471 require C4::Charset;
2472 return unless defined $xml and $xml ne "";
2473 my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8');
2474 my $unlinked_subfields = [];
2475 my @fields = $marc->fields();
2476 if ($#fields > -1) {
2477 foreach my $subfield ($fields[0]->subfields()) {
2478 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2479 }
2480 }
2481 return $unlinked_subfields;
2482}
2483
2484=head2 GetAnalyticsCount
2485
2486 $count= &GetAnalyticsCount($itemnumber)
2487
2488counts Usage of itemnumber in Analytical bibliorecords.
2489
2490=cut
2491
2492sub GetAnalyticsCount {
2493 my ($itemnumber) = @_;
2494 require C4::Search;
2495
2496 ### ZOOM search here
2497 my $query;
2498 $query= "hi=".$itemnumber;
2499 my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
2500 return ($result);
2501}
2502
2503=head2 GetItemHolds
2504
2505=over 4
2506$holds = &GetItemHolds($biblionumber, $itemnumber);
2507
2508=back
2509
2510This function return the count of holds with $biblionumber and $itemnumber
2511
2512=cut
2513
2514sub GetItemHolds {
2515 my ($biblionumber, $itemnumber) = @_;
2516 my $holds;
2517 my $dbh = C4::Context->dbh;
2518 my $query = "SELECT count(*)
2519 FROM reserves
2520 WHERE biblionumber=? AND itemnumber=?";
2521 my $sth = $dbh->prepare($query);
2522 $sth->execute($biblionumber, $itemnumber);
2523 $holds = $sth->fetchrow;
2524 return $holds;
2525}
2526
2527# Return the list of the column names of items table
2528sub _get_items_columns {
2529 my $dbh = C4::Context->dbh;
2530 my $sth = $dbh->column_info(undef, undef, 'items', '%');
2531 $sth->execute;
2532 my $results = $sth->fetchall_hashref('COLUMN_NAME');
2533 return keys %$results;
2534}
2535
2536=head2 SearchItems
2537
2538 my $items = SearchItems($field, $value);
2539
2540SearchItems will search for items on a specific given field.
2541For instance you can search all items with a specific stocknumber like this:
2542
2543 my $items = SearchItems('stocknumber', $stocknumber);
2544
2545=cut
2546
2547sub SearchItems {
2548 my ($field, $value) = @_;
2549
2550 my $dbh = C4::Context->dbh;
2551 my @columns = _get_items_columns;
2552 my $results = [];
2553 if(0 < grep /^$field$/, @columns) {
2554 my $query = "SELECT $field FROM items WHERE $field = ?";
2555 my $sth = $dbh->prepare( $query );
2556 $sth->execute( $value );
2557 $results = $sth->fetchall_arrayref({});
2558 }
2559 return $results;
2560}
2561
2562
2563=head1 OTHER FUNCTIONS
2564
2565=head2 _find_value
2566
2567 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2568
2569Find the given $subfield in the given $tag in the given
2570MARC::Record $record. If the subfield is found, returns
2571the (indicators, value) pair; otherwise, (undef, undef) is
2572returned.
2573
2574PROPOSITION :
2575Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2576I suggest we export it from this module.
2577
2578=cut
2579
2580sub _find_value {
2581 my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2582 my @result;
2583 my $indicator;
2584 if ( $tagfield < 10 ) {
2585 if ( $record->field($tagfield) ) {
2586 push @result, $record->field($tagfield)->data();
2587 } else {
2588 push @result, "";
2589 }
2590 } else {
2591 foreach my $field ( $record->field($tagfield) ) {
2592 my @subfields = $field->subfields();
2593 foreach my $subfield (@subfields) {
2594 if ( @$subfield[0] eq $insubfield ) {
2595 push @result, @$subfield[1];
2596 $indicator = $field->indicator(1) . $field->indicator(2);
2597 }
2598 }
2599 }
2600 }
2601 return ( $indicator, @result );
2602}
2603
2604
2605=head2 PrepareItemrecordDisplay
2606
2607 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2608
2609Returns a hash with all the fields for Display a given item data in a template
2610
2611The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2612
2613=cut
2614
2615sub PrepareItemrecordDisplay {
2616
2617 my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2618
2619 my $dbh = C4::Context->dbh;
2620 $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2621 my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2622 my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2623
2624 # return nothing if we don't have found an existing framework.
2625 return q{} unless $tagslib;
2626 my $itemrecord;
2627 if ($itemnum) {
2628 $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
2629 }
2630 my @loop_data;
2631
2632 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
2633 my $query = qq{
2634 SELECT authorised_value,lib FROM authorised_values
2635 };
2636 $query .= qq{
2637 LEFT JOIN authorised_values_branches ON ( id = av_id )
2638 } if $branch_limit;
2639 $query .= qq{
2640 WHERE category = ?
2641 };
2642 $query .= qq{ AND ( branchcode = ? OR branchcode IS NULL )} if $branch_limit;
2643 $query .= qq{ ORDER BY lib};
2644 my $authorised_values_sth = $dbh->prepare( $query );
2645 foreach my $tag ( sort keys %{$tagslib} ) {
2646 my $previous_tag = '';
2647 if ( $tag ne '' ) {
2648
2649 # loop through each subfield
2650 my $cntsubf;
2651 foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2652 next if ( subfield_is_koha_internal_p($subfield) );
2653 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2654 my %subfield_data;
2655 $subfield_data{tag} = $tag;
2656 $subfield_data{subfield} = $subfield;
2657 $subfield_data{countsubfield} = $cntsubf++;
2658 $subfield_data{kohafield} = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2659 $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2660
2661 # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2662 $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2663 $subfield_data{mandatory} = $tagslib->{$tag}->{$subfield}->{mandatory};
2664 $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2665 $subfield_data{hidden} = "display:none"
2666 if ( ( $tagslib->{$tag}->{$subfield}->{hidden} > 4 )
2667 || ( $tagslib->{$tag}->{$subfield}->{hidden} < -4 ) );
2668 my ( $x, $defaultvalue );
2669 if ($itemrecord) {
2670 ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2671 }
2672 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2673 if ( !defined $defaultvalue ) {
2674 $defaultvalue = q||;
2675 } else {
2676 $defaultvalue =~ s/"/&quot;/g;
2677 }
2678
2679 # search for itemcallnumber if applicable
2680 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2681 && C4::Context->preference('itemcallnumber') ) {
2682 my $CNtag = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2683 my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2684 if ( $itemrecord and my $field = $itemrecord->field($CNtag) ) {
2685 $defaultvalue = $field->subfield($CNsubfield);
2686 }
2687 }
2688 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2689 && $defaultvalues
2690 && $defaultvalues->{'callnumber'} ) {
2691 if( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ){
2692 # if the item record exists, only use default value if the item has no callnumber
2693 $defaultvalue = $defaultvalues->{callnumber};
2694 } elsif ( !$itemrecord and $defaultvalues ) {
2695 # if the item record *doesn't* exists, always use the default value
2696 $defaultvalue = $defaultvalues->{callnumber};
2697 }
2698 }
2699 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2700 && $defaultvalues
2701 && $defaultvalues->{'branchcode'} ) {
2702 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2703 $defaultvalue = $defaultvalues->{branchcode};
2704 }
2705 }
2706 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2707 && $defaultvalues
2708 && $defaultvalues->{'location'} ) {
2709
2710 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2711 # if the item record exists, only use default value if the item has no locationr
2712 $defaultvalue = $defaultvalues->{location};
2713 } elsif ( !$itemrecord and $defaultvalues ) {
2714 # if the item record *doesn't* exists, always use the default value
2715 $defaultvalue = $defaultvalues->{location};
2716 }
2717 }
2718 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2719 my @authorised_values;
2720 my %authorised_lib;
2721
2722 # builds list, depending on authorised value...
2723 #---- branch
2724 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2725 if ( ( C4::Context->preference("IndependentBranches") )
2726 && !C4::Context->IsSuperLibrarian() ) {
2727 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2728 $sth->execute( C4::Context->userenv->{branch} );
2729 push @authorised_values, ""
2730 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2731 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2732 push @authorised_values, $branchcode;
2733 $authorised_lib{$branchcode} = $branchname;
2734 }
2735 } else {
2736 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2737 $sth->execute;
2738 push @authorised_values, ""
2739 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2740 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2741 push @authorised_values, $branchcode;
2742 $authorised_lib{$branchcode} = $branchname;
2743 }
2744 }
2745
2746 $defaultvalue = C4::Context->userenv ? C4::Context->userenv->{branch} : undef;
2747 if ( $defaultvalues and $defaultvalues->{branchcode} ) {
2748 $defaultvalue = $defaultvalues->{branchcode};
2749 }
2750
2751 #----- itemtypes
2752 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2753 my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2754 $sth->execute;
2755 push @authorised_values, ""
2756 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2757 while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2758 push @authorised_values, $itemtype;
2759 $authorised_lib{$itemtype} = $description;
2760 }
2761 #---- class_sources
2762 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2763 push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2764
2765 my $class_sources = GetClassSources();
2766 my $default_source = C4::Context->preference("DefaultClassificationSource");
2767
2768 foreach my $class_source (sort keys %$class_sources) {
2769 next unless $class_sources->{$class_source}->{'used'} or
2770 ($class_source eq $default_source);
2771 push @authorised_values, $class_source;
2772 $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2773 }
2774
2775 $defaultvalue = $default_source;
2776
2777 #---- "true" authorised value
2778 } else {
2779 $authorised_values_sth->execute(
2780 $tagslib->{$tag}->{$subfield}->{authorised_value},
2781 $branch_limit ? $branch_limit : ()
2782 );
2783 push @authorised_values, ""
2784 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2785 while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2786 push @authorised_values, $value;
2787 $authorised_lib{$value} = $lib;
2788 }
2789 }
2790 $subfield_data{marc_value} = CGI::scrolling_list(
2791 -name => 'field_value',
2792 -values => \@authorised_values,
2793 -default => "$defaultvalue",
2794 -labels => \%authorised_lib,
2795 -size => 1,
2796 -tabindex => '',
2797 -multiple => 0,
2798 );
2799 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2800 # opening plugin
2801 my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2802 if (do $plugin) {
2803 my $extended_param = plugin_parameters( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2804 my ( $function_name, $javascript ) = plugin_javascript( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2805 $subfield_data{random} = int(rand(1000000)); # why do we need 2 different randoms?
2806 $subfield_data{marc_value} = qq[<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255"
2807 onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2808 onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2809 <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2810 $javascript];
2811 } else {
2812 warn "Plugin Failed: $plugin";
2813 $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" />); # supply default input form
2814 }
2815 }
2816 elsif ( $tag eq '' ) { # it's an hidden field
2817 $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" value="$defaultvalue" />);
2818 }
2819 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ?
2820 $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255" value="$defaultvalue" />);
2821 }
2822 elsif ( length($defaultvalue) > 100
2823 or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2824 300 <= $tag && $tag < 400 && $subfield eq 'a' )
2825 or (C4::Context->preference("marcflavour") eq "MARC21" and
2826 500 <= $tag && $tag < 600 )
2827 ) {
2828 # oversize field (textarea)
2829 $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="50" maxlength="255">$defaultvalue</textarea>\n");
2830 } else {
2831 $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2832 }
2833 push( @loop_data, \%subfield_data );
2834 }
2835 }
2836 }
2837 my $itemnumber;
2838 if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
2839 $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
2840 }
2841 return {
2842 'itemtagfield' => $itemtagfield,
2843 'itemtagsubfield' => $itemtagsubfield,
2844 'itemnumber' => $itemnumber,
2845 'iteminformation' => \@loop_data
2846 };
2847}
2848
284916µs1;