Bug 10622 - [SIGNED OFF] Followup: Add --sip, --nosip and --help to man page
[koha.git] / C4 / Items.pm
blob8116c224dc7e00c9bea9836f689b60e384bf2bdf
1 package C4::Items;
3 # Copyright 2007 LibLime, Inc.
4 # Parts Copyright Biblibre 2010
6 # This file is part of Koha.
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.
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.
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.
21 use strict;
22 #use warnings; FIXME - Bug 2505
24 use Carp;
25 use C4::Context;
26 use C4::Koha;
27 use C4::Biblio;
28 use C4::Dates qw/format_date format_date_in_iso/;
29 use MARC::Record;
30 use C4::ClassSource;
31 use C4::Log;
32 use List::MoreUtils qw/any/;
33 use YAML qw/Load/;
34 use Data::Dumper; # used as part of logging item record changes, not just for
35 # debugging; so please don't remove this
37 use vars qw($VERSION @ISA @EXPORT);
39 BEGIN {
40 $VERSION = 3.07.00.049;
42 require Exporter;
43 @ISA = qw( Exporter );
45 # function exports
46 @EXPORT = qw(
47 GetItem
48 AddItemFromMarc
49 AddItem
50 AddItemBatchFromMarc
51 ModItemFromMarc
52 Item2Marc
53 ModItem
54 ModDateLastSeen
55 ModItemTransfer
56 DelItem
58 CheckItemPreSave
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
80 CartToShelf
81 ShelfToCart
83 GetAnalyticsCount
84 GetItemHolds
86 SearchItems
88 PrepareItemrecordDisplay
93 =head1 NAME
95 C4::Items - item management functions
97 =head1 DESCRIPTION
99 This module contains an API for manipulating item
100 records in Koha, and is used by cataloguing, circulation,
101 acquisitions, and serials management.
103 A Koha item record is stored in two places: the
104 items table and embedded in a MARC tag in the XML
105 version of the associated bib record in C<biblioitems.marcxml>.
106 This is done to allow the item information to be readily
107 indexed (e.g., by Zebra), but means that each item
108 modification transaction must keep the items table
109 and the MARC XML in sync at all times.
111 Consequently, all code that creates, modifies, or deletes
112 item records B<must> use an appropriate function from
113 C<C4::Items>. If no existing function is suitable, it is
114 better to add one to C<C4::Items> than to use add
115 one-off SQL statements to add or modify items.
117 The items table will be considered authoritative. In other
118 words, if there is ever a discrepancy between the items
119 table and the MARC XML, the items table should be considered
120 accurate.
122 =head1 HISTORICAL NOTE
124 Most of the functions in C<C4::Items> were originally in
125 the C<C4::Biblio> module.
127 =head1 CORE EXPORTED FUNCTIONS
129 The following functions are meant for use by users
130 of C<C4::Items>
132 =cut
134 =head2 GetItem
136 $item = GetItem($itemnumber,$barcode,$serial);
138 Return item information, for a given itemnumber or barcode.
139 The return value is a hashref mapping item column
140 names to values. If C<$serial> is true, include serial publication data.
142 =cut
144 sub GetItem {
145 my ($itemnumber,$barcode, $serial) = @_;
146 my $dbh = C4::Context->dbh;
147 my $data;
148 if ($itemnumber) {
149 my $sth = $dbh->prepare("
150 SELECT * FROM items
151 WHERE itemnumber = ?");
152 $sth->execute($itemnumber);
153 $data = $sth->fetchrow_hashref;
154 } else {
155 my $sth = $dbh->prepare("
156 SELECT * FROM items
157 WHERE barcode = ?"
159 $sth->execute($barcode);
160 $data = $sth->fetchrow_hashref;
162 if ( $serial) {
163 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
164 $ssth->execute($data->{'itemnumber'}) ;
165 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
167 #if we don't have an items.itype, use biblioitems.itemtype.
168 if( ! $data->{'itype'} ) {
169 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
170 $sth->execute($data->{'biblionumber'});
171 ($data->{'itype'}) = $sth->fetchrow_array;
173 return $data;
174 } # sub GetItem
176 =head2 CartToShelf
178 CartToShelf($itemnumber);
180 Set the current shelving location of the item record
181 to its stored permanent shelving location. This is
182 primarily used to indicate when an item whose current
183 location is a special processing ('PROC') or shelving cart
184 ('CART') location is back in the stacks.
186 =cut
188 sub CartToShelf {
189 my ( $itemnumber ) = @_;
191 unless ( $itemnumber ) {
192 croak "FAILED CartToShelf() - no itemnumber supplied";
195 my $item = GetItem($itemnumber);
196 if ( $item->{location} eq 'CART' ) {
197 $item->{location} = $item->{permanent_location};
198 ModItem($item, undef, $itemnumber);
202 =head2 ShelfToCart
204 ShelfToCart($itemnumber);
206 Set the current shelving location of the item
207 to shelving cart ('CART').
209 =cut
211 sub ShelfToCart {
212 my ( $itemnumber ) = @_;
214 unless ( $itemnumber ) {
215 croak "FAILED ShelfToCart() - no itemnumber supplied";
218 my $item = GetItem($itemnumber);
219 $item->{'location'} = 'CART';
220 ModItem($item, undef, $itemnumber);
223 =head2 AddItemFromMarc
225 my ($biblionumber, $biblioitemnumber, $itemnumber)
226 = AddItemFromMarc($source_item_marc, $biblionumber);
228 Given a MARC::Record object containing an embedded item
229 record and a biblionumber, create a new item record.
231 =cut
233 sub AddItemFromMarc {
234 my ( $source_item_marc, $biblionumber ) = @_;
235 my $dbh = C4::Context->dbh;
237 # parse item hash from MARC
238 my $frameworkcode = GetFrameworkCode( $biblionumber );
239 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
241 my $localitemmarc=MARC::Record->new;
242 $localitemmarc->append_fields($source_item_marc->field($itemtag));
243 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
244 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
245 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
248 =head2 AddItem
250 my ($biblionumber, $biblioitemnumber, $itemnumber)
251 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
253 Given a hash containing item column names as keys,
254 create a new Koha item record.
256 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
257 do not need to be supplied for general use; they exist
258 simply to allow them to be picked up from AddItemFromMarc.
260 The final optional parameter, C<$unlinked_item_subfields>, contains
261 an arrayref containing subfields present in the original MARC
262 representation of the item (e.g., from the item editor) that are
263 not mapped to C<items> columns directly but should instead
264 be stored in C<items.more_subfields_xml> and included in
265 the biblio items tag for display and indexing.
267 =cut
269 sub AddItem {
270 my $item = shift;
271 my $biblionumber = shift;
273 my $dbh = @_ ? shift : C4::Context->dbh;
274 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
275 my $unlinked_item_subfields;
276 if (@_) {
277 $unlinked_item_subfields = shift
280 # needs old biblionumber and biblioitemnumber
281 $item->{'biblionumber'} = $biblionumber;
282 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
283 $sth->execute( $item->{'biblionumber'} );
284 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
286 _set_defaults_for_add($item);
287 _set_derived_columns_for_add($item);
288 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
289 # FIXME - checks here
290 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
291 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
292 $itype_sth->execute( $item->{'biblionumber'} );
293 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
296 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
297 $item->{'itemnumber'} = $itemnumber;
299 ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver" );
301 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
303 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
306 =head2 AddItemBatchFromMarc
308 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record,
309 $biblionumber, $biblioitemnumber, $frameworkcode);
311 Efficiently create item records from a MARC biblio record with
312 embedded item fields. This routine is suitable for batch jobs.
314 This API assumes that the bib record has already been
315 saved to the C<biblio> and C<biblioitems> tables. It does
316 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
317 are populated, but it will do so via a call to ModBibiloMarc.
319 The goal of this API is to have a similar effect to using AddBiblio
320 and AddItems in succession, but without inefficient repeated
321 parsing of the MARC XML bib record.
323 This function returns an arrayref of new itemsnumbers and an arrayref of item
324 errors encountered during the processing. Each entry in the errors
325 list is a hashref containing the following keys:
327 =over
329 =item item_sequence
331 Sequence number of original item tag in the MARC record.
333 =item item_barcode
335 Item barcode, provide to assist in the construction of
336 useful error messages.
338 =item error_code
340 Code representing the error condition. Can be 'duplicate_barcode',
341 'invalid_homebranch', or 'invalid_holdingbranch'.
343 =item error_information
345 Additional information appropriate to the error condition.
347 =back
349 =cut
351 sub AddItemBatchFromMarc {
352 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
353 my $error;
354 my @itemnumbers = ();
355 my @errors = ();
356 my $dbh = C4::Context->dbh;
358 # We modify the record, so lets work on a clone so we don't change the
359 # original.
360 $record = $record->clone();
361 # loop through the item tags and start creating items
362 my @bad_item_fields = ();
363 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
364 my $item_sequence_num = 0;
365 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
366 $item_sequence_num++;
367 # we take the item field and stick it into a new
368 # MARC record -- this is required so far because (FIXME)
369 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
370 # and there is no TransformMarcFieldToKoha
371 my $temp_item_marc = MARC::Record->new();
372 $temp_item_marc->append_fields($item_field);
374 # add biblionumber and biblioitemnumber
375 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
376 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
377 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
378 $item->{'biblionumber'} = $biblionumber;
379 $item->{'biblioitemnumber'} = $biblioitemnumber;
381 # check for duplicate barcode
382 my %item_errors = CheckItemPreSave($item);
383 if (%item_errors) {
384 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
385 push @bad_item_fields, $item_field;
386 next ITEMFIELD;
389 _set_defaults_for_add($item);
390 _set_derived_columns_for_add($item);
391 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
392 warn $error if $error;
393 push @itemnumbers, $itemnumber; # FIXME not checking error
394 $item->{'itemnumber'} = $itemnumber;
396 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
398 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
399 $item_field->replace_with($new_item_marc->field($itemtag));
402 # remove any MARC item fields for rejected items
403 foreach my $item_field (@bad_item_fields) {
404 $record->delete_field($item_field);
407 # update the MARC biblio
408 # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
410 return (\@itemnumbers, \@errors);
413 =head2 ModItemFromMarc
415 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
417 This function updates an item record based on a supplied
418 C<MARC::Record> object containing an embedded item field.
419 This API is meant for the use of C<additem.pl>; for
420 other purposes, C<ModItem> should be used.
422 This function uses the hash %default_values_for_mod_from_marc,
423 which contains default values for item fields to
424 apply when modifying an item. This is needed beccause
425 if an item field's value is cleared, TransformMarcToKoha
426 does not include the column in the
427 hash that's passed to ModItem, which without
428 use of this hash makes it impossible to clear
429 an item field's value. See bug 2466.
431 Note that only columns that can be directly
432 changed from the cataloging and serials
433 item editors are included in this hash.
435 Returns item record
437 =cut
439 my %default_values_for_mod_from_marc = (
440 barcode => undef,
441 booksellerid => undef,
442 ccode => undef,
443 'items.cn_source' => undef,
444 coded_location_qualifier => undef,
445 copynumber => undef,
446 damaged => 0,
447 # dateaccessioned => undef,
448 enumchron => undef,
449 holdingbranch => undef,
450 homebranch => undef,
451 itemcallnumber => undef,
452 itemlost => 0,
453 itemnotes => undef,
454 itype => undef,
455 location => undef,
456 permanent_location => undef,
457 materials => undef,
458 notforloan => 0,
459 paidfor => undef,
460 price => undef,
461 replacementprice => undef,
462 replacementpricedate => undef,
463 restricted => undef,
464 stack => undef,
465 stocknumber => undef,
466 uri => undef,
467 withdrawn => 0,
470 sub ModItemFromMarc {
471 my $item_marc = shift;
472 my $biblionumber = shift;
473 my $itemnumber = shift;
475 my $dbh = C4::Context->dbh;
476 my $frameworkcode = GetFrameworkCode($biblionumber);
477 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
479 my $localitemmarc = MARC::Record->new;
480 $localitemmarc->append_fields( $item_marc->field($itemtag) );
481 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
482 foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
483 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
485 my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
487 ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
488 return $item;
491 =head2 ModItem
493 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
495 Change one or more columns in an item record and update
496 the MARC representation of the item.
498 The first argument is a hashref mapping from item column
499 names to the new values. The second and third arguments
500 are the biblionumber and itemnumber, respectively.
502 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
503 an arrayref containing subfields present in the original MARC
504 representation of the item (e.g., from the item editor) that are
505 not mapped to C<items> columns directly but should instead
506 be stored in C<items.more_subfields_xml> and included in
507 the biblio items tag for display and indexing.
509 If one of the changed columns is used to calculate
510 the derived value of a column such as C<items.cn_sort>,
511 this routine will perform the necessary calculation
512 and set the value.
514 =cut
516 sub ModItem {
517 my $item = shift;
518 my $biblionumber = shift;
519 my $itemnumber = shift;
521 # if $biblionumber is undefined, get it from the current item
522 unless (defined $biblionumber) {
523 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
526 my $dbh = @_ ? shift : C4::Context->dbh;
527 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
529 my $unlinked_item_subfields;
530 if (@_) {
531 $unlinked_item_subfields = shift;
532 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
535 $item->{'itemnumber'} = $itemnumber or return;
537 $item->{onloan} = undef if $item->{itemlost};
539 _set_derived_columns_for_mod($item);
540 _do_column_fixes_for_mod($item);
541 # FIXME add checks
542 # duplicate barcode
543 # attempt to change itemnumber
544 # attempt to change biblionumber (if we want
545 # an API to relink an item to a different bib,
546 # it should be a separate function)
548 # update items table
549 _koha_modify_item($item);
551 # request that bib be reindexed so that searching on current
552 # item status is possible
553 ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
555 logaction("CATALOGUING", "MODIFY", $itemnumber, Dumper($item)) if C4::Context->preference("CataloguingLog");
558 =head2 ModItemTransfer
560 ModItemTransfer($itenumber, $frombranch, $tobranch);
562 Marks an item as being transferred from one branch
563 to another.
565 =cut
567 sub ModItemTransfer {
568 my ( $itemnumber, $frombranch, $tobranch ) = @_;
570 my $dbh = C4::Context->dbh;
572 # Remove the 'shelving cart' location status if it is being used.
573 CartToShelf( $itemnumber ) if ( C4::Context->preference("ReturnToShelvingCart") );
575 #new entry in branchtransfers....
576 my $sth = $dbh->prepare(
577 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
578 VALUES (?, ?, NOW(), ?)");
579 $sth->execute($itemnumber, $frombranch, $tobranch);
581 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
582 ModDateLastSeen($itemnumber);
583 return;
586 =head2 ModDateLastSeen
588 ModDateLastSeen($itemnum);
590 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
591 C<$itemnum> is the item number
593 =cut
595 sub ModDateLastSeen {
596 my ($itemnumber) = @_;
598 my $today = C4::Dates->new();
599 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
602 =head2 DelItem
604 DelItem($dbh, $biblionumber, $itemnumber);
606 Exported function (core API) for deleting an item record in Koha.
608 =cut
610 sub DelItem {
611 my ( $dbh, $biblionumber, $itemnumber ) = @_;
613 # FIXME check the item has no current issues
615 _koha_delete_item( $dbh, $itemnumber );
617 # get the MARC record
618 my $record = GetMarcBiblio($biblionumber);
619 ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
621 # backup the record
622 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
623 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
624 # This last update statement makes that the timestamp column in deleteditems is updated too. If you remove these lines, please add a line to update the timestamp separately. See Bugzilla report 7146 and Biblio.pm (DelBiblio).
626 #search item field code
627 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
630 =head2 CheckItemPreSave
632 my $item_ref = TransformMarcToKoha($marc, 'items');
633 # do stuff
634 my %errors = CheckItemPreSave($item_ref);
635 if (exists $errors{'duplicate_barcode'}) {
636 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
637 } elsif (exists $errors{'invalid_homebranch'}) {
638 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
639 } elsif (exists $errors{'invalid_holdingbranch'}) {
640 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
641 } else {
642 print "item is OK";
645 Given a hashref containing item fields, determine if it can be
646 inserted or updated in the database. Specifically, checks for
647 database integrity issues, and returns a hash containing any
648 of the following keys, if applicable.
650 =over 2
652 =item duplicate_barcode
654 Barcode, if it duplicates one already found in the database.
656 =item invalid_homebranch
658 Home branch, if not defined in branches table.
660 =item invalid_holdingbranch
662 Holding branch, if not defined in branches table.
664 =back
666 This function does NOT implement any policy-related checks,
667 e.g., whether current operator is allowed to save an
668 item that has a given branch code.
670 =cut
672 sub CheckItemPreSave {
673 my $item_ref = shift;
674 require C4::Branch;
676 my %errors = ();
678 # check for duplicate barcode
679 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
680 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
681 if ($existing_itemnumber) {
682 if (!exists $item_ref->{'itemnumber'} # new item
683 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
684 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
689 # check for valid home branch
690 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
691 my $branch_name = C4::Branch::GetBranchName($item_ref->{'homebranch'});
692 unless (defined $branch_name) {
693 # relies on fact that branches.branchname is a non-NULL column,
694 # so GetBranchName returns undef only if branch does not exist
695 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
699 # check for valid holding branch
700 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
701 my $branch_name = C4::Branch::GetBranchName($item_ref->{'holdingbranch'});
702 unless (defined $branch_name) {
703 # relies on fact that branches.branchname is a non-NULL column,
704 # so GetBranchName returns undef only if branch does not exist
705 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
709 return %errors;
713 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
715 The following functions provide various ways of
716 getting an item record, a set of item records, or
717 lists of authorized values for certain item fields.
719 Some of the functions in this group are candidates
720 for refactoring -- for example, some of the code
721 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
722 has copy-and-paste work.
724 =cut
726 =head2 GetItemStatus
728 $itemstatushash = GetItemStatus($fwkcode);
730 Returns a list of valid values for the
731 C<items.notforloan> field.
733 NOTE: does B<not> return an individual item's
734 status.
736 Can be MARC dependant.
737 fwkcode is optional.
738 But basically could be can be loan or not
739 Create a status selector with the following code
741 =head3 in PERL SCRIPT
743 my $itemstatushash = getitemstatus;
744 my @itemstatusloop;
745 foreach my $thisstatus (keys %$itemstatushash) {
746 my %row =(value => $thisstatus,
747 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
749 push @itemstatusloop, \%row;
751 $template->param(statusloop=>\@itemstatusloop);
753 =head3 in TEMPLATE
755 <select name="statusloop" id="statusloop">
756 <option value="">Default</option>
757 [% FOREACH statusloo IN statusloop %]
758 [% IF ( statusloo.selected ) %]
759 <option value="[% statusloo.value %]" selected="selected">[% statusloo.statusname %]</option>
760 [% ELSE %]
761 <option value="[% statusloo.value %]">[% statusloo.statusname %]</option>
762 [% END %]
763 [% END %]
764 </select>
766 =cut
768 sub GetItemStatus {
770 # returns a reference to a hash of references to status...
771 my ($fwk) = @_;
772 my %itemstatus;
773 my $dbh = C4::Context->dbh;
774 my $sth;
775 $fwk = '' unless ($fwk);
776 my ( $tag, $subfield ) =
777 GetMarcFromKohaField( "items.notforloan", $fwk );
778 if ( $tag and $subfield ) {
779 my $sth =
780 $dbh->prepare(
781 "SELECT authorised_value
782 FROM marc_subfield_structure
783 WHERE tagfield=?
784 AND tagsubfield=?
785 AND frameworkcode=?
788 $sth->execute( $tag, $subfield, $fwk );
789 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
790 my $authvalsth =
791 $dbh->prepare(
792 "SELECT authorised_value,lib
793 FROM authorised_values
794 WHERE category=?
795 ORDER BY lib
798 $authvalsth->execute($authorisedvaluecat);
799 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
800 $itemstatus{$authorisedvalue} = $lib;
802 return \%itemstatus;
803 exit 1;
805 else {
807 #No authvalue list
808 # build default
812 #No authvalue list
813 #build default
814 $itemstatus{"1"} = "Not For Loan";
815 return \%itemstatus;
818 =head2 GetItemLocation
820 $itemlochash = GetItemLocation($fwk);
822 Returns a list of valid values for the
823 C<items.location> field.
825 NOTE: does B<not> return an individual item's
826 location.
828 where fwk stands for an optional framework code.
829 Create a location selector with the following code
831 =head3 in PERL SCRIPT
833 my $itemlochash = getitemlocation;
834 my @itemlocloop;
835 foreach my $thisloc (keys %$itemlochash) {
836 my $selected = 1 if $thisbranch eq $branch;
837 my %row =(locval => $thisloc,
838 selected => $selected,
839 locname => $itemlochash->{$thisloc},
841 push @itemlocloop, \%row;
843 $template->param(itemlocationloop => \@itemlocloop);
845 =head3 in TEMPLATE
847 <select name="location">
848 <option value="">Default</option>
849 <!-- TMPL_LOOP name="itemlocationloop" -->
850 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
851 <!-- /TMPL_LOOP -->
852 </select>
854 =cut
856 sub GetItemLocation {
858 # returns a reference to a hash of references to location...
859 my ($fwk) = @_;
860 my %itemlocation;
861 my $dbh = C4::Context->dbh;
862 my $sth;
863 $fwk = '' unless ($fwk);
864 my ( $tag, $subfield ) =
865 GetMarcFromKohaField( "items.location", $fwk );
866 if ( $tag and $subfield ) {
867 my $sth =
868 $dbh->prepare(
869 "SELECT authorised_value
870 FROM marc_subfield_structure
871 WHERE tagfield=?
872 AND tagsubfield=?
873 AND frameworkcode=?"
875 $sth->execute( $tag, $subfield, $fwk );
876 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
877 my $authvalsth =
878 $dbh->prepare(
879 "SELECT authorised_value,lib
880 FROM authorised_values
881 WHERE category=?
882 ORDER BY lib"
884 $authvalsth->execute($authorisedvaluecat);
885 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
886 $itemlocation{$authorisedvalue} = $lib;
888 return \%itemlocation;
889 exit 1;
891 else {
893 #No authvalue list
894 # build default
898 #No authvalue list
899 #build default
900 $itemlocation{"1"} = "Not For Loan";
901 return \%itemlocation;
904 =head2 GetLostItems
906 $items = GetLostItems( $where, $orderby );
908 This function gets a list of lost items.
910 =over 2
912 =item input:
914 C<$where> is a hashref. it containts a field of the items table as key
915 and the value to match as value. For example:
917 { barcode => 'abc123',
918 homebranch => 'CPL', }
920 C<$orderby> is a field of the items table by which the resultset
921 should be orderd.
923 =item return:
925 C<$items> is a reference to an array full of hashrefs with columns
926 from the "items" table as keys.
928 =item usage in the perl script:
930 my $where = { barcode => '0001548' };
931 my $items = GetLostItems( $where, "homebranch" );
932 $template->param( itemsloop => $items );
934 =back
936 =cut
938 sub GetLostItems {
939 # Getting input args.
940 my $where = shift;
941 my $orderby = shift;
942 my $dbh = C4::Context->dbh;
944 my $query = "
945 SELECT title, author, lib, itemlost, authorised_value, barcode, datelastseen, price, replacementprice, homebranch,
946 itype, itemtype, holdingbranch, location, itemnotes, items.biblionumber as biblionumber
947 FROM items
948 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
949 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
950 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
951 WHERE
952 authorised_values.category = 'LOST'
953 AND itemlost IS NOT NULL
954 AND itemlost <> 0
956 my @query_parameters;
957 foreach my $key (keys %$where) {
958 $query .= " AND $key LIKE ?";
959 push @query_parameters, "%$where->{$key}%";
961 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
963 if ( defined $orderby && grep($orderby, @ordervalues)) {
964 $query .= ' ORDER BY '.$orderby;
967 my $sth = $dbh->prepare($query);
968 $sth->execute( @query_parameters );
969 my $items = [];
970 while ( my $row = $sth->fetchrow_hashref ){
971 push @$items, $row;
973 return $items;
976 =head2 GetItemsForInventory
978 $itemlist = GetItemsForInventory($minlocation, $maxlocation,
979 $location, $itemtype $datelastseen, $branch,
980 $offset, $size, $statushash);
982 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
984 The sub returns a reference to a list of hashes, each containing
985 itemnumber, author, title, barcode, item callnumber, and date last
986 seen. It is ordered by callnumber then title.
988 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
989 the datelastseen can be used to specify that you want to see items not seen since a past date only.
990 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
991 $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.
993 =cut
995 sub GetItemsForInventory {
996 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
997 my $dbh = C4::Context->dbh;
998 my ( @bind_params, @where_strings );
1000 my $query = <<'END_SQL';
1001 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
1002 FROM items
1003 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1004 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1005 END_SQL
1006 if ($statushash){
1007 for my $authvfield (keys %$statushash){
1008 if ( scalar @{$statushash->{$authvfield}} > 0 ){
1009 my $joinedvals = join ',', @{$statushash->{$authvfield}};
1010 push @where_strings, "$authvfield in (" . $joinedvals . ")";
1015 if ($minlocation) {
1016 push @where_strings, 'itemcallnumber >= ?';
1017 push @bind_params, $minlocation;
1020 if ($maxlocation) {
1021 push @where_strings, 'itemcallnumber <= ?';
1022 push @bind_params, $maxlocation;
1025 if ($datelastseen) {
1026 $datelastseen = format_date_in_iso($datelastseen);
1027 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1028 push @bind_params, $datelastseen;
1031 if ( $location ) {
1032 push @where_strings, 'items.location = ?';
1033 push @bind_params, $location;
1036 if ( $branchcode ) {
1037 if($branch eq "homebranch"){
1038 push @where_strings, 'items.homebranch = ?';
1039 }else{
1040 push @where_strings, 'items.holdingbranch = ?';
1042 push @bind_params, $branchcode;
1045 if ( $itemtype ) {
1046 push @where_strings, 'biblioitems.itemtype = ?';
1047 push @bind_params, $itemtype;
1050 if ( $ignoreissued) {
1051 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1052 push @where_strings, 'issues.date_due IS NULL';
1055 if ( @where_strings ) {
1056 $query .= 'WHERE ';
1057 $query .= join ' AND ', @where_strings;
1059 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1060 my $sth = $dbh->prepare($query);
1061 $sth->execute( @bind_params );
1063 my @results;
1064 $size--;
1065 while ( my $row = $sth->fetchrow_hashref ) {
1066 $offset-- if ($offset);
1067 $row->{datelastseen}=format_date($row->{datelastseen});
1068 if ( ( !$offset ) && $size ) {
1069 push @results, $row;
1070 $size--;
1073 return \@results;
1076 =head2 GetItemsCount
1078 $count = &GetItemsCount( $biblionumber);
1080 This function return count of item with $biblionumber
1082 =cut
1084 sub GetItemsCount {
1085 my ( $biblionumber ) = @_;
1086 my $dbh = C4::Context->dbh;
1087 my $query = "SELECT count(*)
1088 FROM items
1089 WHERE biblionumber=?";
1090 my $sth = $dbh->prepare($query);
1091 $sth->execute($biblionumber);
1092 my $count = $sth->fetchrow;
1093 return ($count);
1096 =head2 GetItemInfosOf
1098 GetItemInfosOf(@itemnumbers);
1100 =cut
1102 sub GetItemInfosOf {
1103 my @itemnumbers = @_;
1105 my $query = '
1106 SELECT *
1107 FROM items
1108 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1110 return get_infos_of( $query, 'itemnumber' );
1113 =head2 GetItemsByBiblioitemnumber
1115 GetItemsByBiblioitemnumber($biblioitemnumber);
1117 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1118 Called by C<C4::XISBN>
1120 =cut
1122 sub GetItemsByBiblioitemnumber {
1123 my ( $bibitem ) = @_;
1124 my $dbh = C4::Context->dbh;
1125 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1126 # Get all items attached to a biblioitem
1127 my $i = 0;
1128 my @results;
1129 $sth->execute($bibitem) || die $sth->errstr;
1130 while ( my $data = $sth->fetchrow_hashref ) {
1131 # Foreach item, get circulation information
1132 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1133 WHERE itemnumber = ?
1134 AND issues.borrowernumber = borrowers.borrowernumber"
1136 $sth2->execute( $data->{'itemnumber'} );
1137 if ( my $data2 = $sth2->fetchrow_hashref ) {
1138 # if item is out, set the due date and who it is out too
1139 $data->{'date_due'} = $data2->{'date_due'};
1140 $data->{'cardnumber'} = $data2->{'cardnumber'};
1141 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1143 else {
1144 # set date_due to blank, so in the template we check itemlost, and withdrawn
1145 $data->{'date_due'} = '';
1146 } # else
1147 # Find the last 3 people who borrowed this item.
1148 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1149 AND old_issues.borrowernumber = borrowers.borrowernumber
1150 ORDER BY returndate desc,timestamp desc LIMIT 3";
1151 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1152 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1153 my $i2 = 0;
1154 while ( my $data2 = $sth2->fetchrow_hashref ) {
1155 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1156 $data->{"card$i2"} = $data2->{'cardnumber'};
1157 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1158 $i2++;
1160 push(@results,$data);
1162 return (\@results);
1165 =head2 GetItemsInfo
1167 @results = GetItemsInfo($biblionumber);
1169 Returns information about items with the given biblionumber.
1171 C<GetItemsInfo> returns a list of references-to-hash. Each element
1172 contains a number of keys. Most of them are attributes from the
1173 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1174 Koha database. Other keys include:
1176 =over 2
1178 =item C<$data-E<gt>{branchname}>
1180 The name (not the code) of the branch to which the book belongs.
1182 =item C<$data-E<gt>{datelastseen}>
1184 This is simply C<items.datelastseen>, except that while the date is
1185 stored in YYYY-MM-DD format in the database, here it is converted to
1186 DD/MM/YYYY format. A NULL date is returned as C<//>.
1188 =item C<$data-E<gt>{datedue}>
1190 =item C<$data-E<gt>{class}>
1192 This is the concatenation of C<biblioitems.classification>, the book's
1193 Dewey code, and C<biblioitems.subclass>.
1195 =item C<$data-E<gt>{ocount}>
1197 I think this is the number of copies of the book available.
1199 =item C<$data-E<gt>{order}>
1201 If this is set, it is set to C<One Order>.
1203 =back
1205 =cut
1207 sub GetItemsInfo {
1208 my ( $biblionumber ) = @_;
1209 my $dbh = C4::Context->dbh;
1210 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1211 my $query = "
1212 SELECT items.*,
1213 biblio.*,
1214 biblioitems.volume,
1215 biblioitems.number,
1216 biblioitems.itemtype,
1217 biblioitems.isbn,
1218 biblioitems.issn,
1219 biblioitems.publicationyear,
1220 biblioitems.publishercode,
1221 biblioitems.volumedate,
1222 biblioitems.volumedesc,
1223 biblioitems.lccn,
1224 biblioitems.url,
1225 items.notforloan as itemnotforloan,
1226 itemtypes.description,
1227 itemtypes.notforloan as notforloan_per_itemtype,
1228 holding.branchurl,
1229 holding.branchname,
1230 holding.opac_info as branch_opac_info
1231 FROM items
1232 LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1233 LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1234 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1235 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1236 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1237 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1238 $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1239 my $sth = $dbh->prepare($query);
1240 $sth->execute($biblionumber);
1241 my $i = 0;
1242 my @results;
1243 my $serial;
1245 my $isth = $dbh->prepare(
1246 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1247 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1248 WHERE itemnumber = ?"
1250 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1251 while ( my $data = $sth->fetchrow_hashref ) {
1252 my $datedue = '';
1253 $isth->execute( $data->{'itemnumber'} );
1254 if ( my $idata = $isth->fetchrow_hashref ) {
1255 $data->{borrowernumber} = $idata->{borrowernumber};
1256 $data->{cardnumber} = $idata->{cardnumber};
1257 $data->{surname} = $idata->{surname};
1258 $data->{firstname} = $idata->{firstname};
1259 $data->{lastreneweddate} = $idata->{lastreneweddate};
1260 $datedue = $idata->{'date_due'};
1261 if (C4::Context->preference("IndependentBranches")){
1262 my $userenv = C4::Context->userenv;
1263 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1264 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1268 if ( $data->{'serial'}) {
1269 $ssth->execute($data->{'itemnumber'}) ;
1270 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1271 $serial = 1;
1273 #get branch information.....
1274 my $bsth = $dbh->prepare(
1275 "SELECT * FROM branches WHERE branchcode = ?
1278 $bsth->execute( $data->{'holdingbranch'} );
1279 if ( my $bdata = $bsth->fetchrow_hashref ) {
1280 $data->{'branchname'} = $bdata->{'branchname'};
1282 $data->{'datedue'} = $datedue;
1284 # get notforloan complete status if applicable
1285 if ( my $code = C4::Koha::GetAuthValCode( 'items.notforloan', $data->{frameworkcode} ) ) {
1286 $data->{notforloanvalue} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan} );
1287 $data->{notforloanvalueopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{itemnotforloan}, 1 );
1290 # get restricted status and description if applicable
1291 if ( my $code = C4::Koha::GetAuthValCode( 'items.restricted', $data->{frameworkcode} ) ) {
1292 $data->{restrictedopac} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted}, 1 );
1293 $data->{restricted} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{restricted} );
1296 # my stack procedures
1297 if ( my $code = C4::Koha::GetAuthValCode( 'items.stack', $data->{frameworkcode} ) ) {
1298 $data->{stack} = C4::Koha::GetKohaAuthorisedValueLib( $code, $data->{stack} );
1300 # Find the last 3 people who borrowed this item.
1301 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1302 WHERE itemnumber = ?
1303 AND old_issues.borrowernumber = borrowers.borrowernumber
1304 ORDER BY returndate DESC
1305 LIMIT 3");
1306 $sth2->execute($data->{'itemnumber'});
1307 my $ii = 0;
1308 while (my $data2 = $sth2->fetchrow_hashref()) {
1309 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1310 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1311 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1312 $ii++;
1315 $results[$i] = $data;
1316 $i++;
1318 if($serial) {
1319 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1320 } else {
1321 return (@results);
1325 =head2 GetItemsLocationInfo
1327 my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1329 Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1331 C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1333 =over 2
1335 =item C<$data-E<gt>{homebranch}>
1337 Branch Name of the item's homebranch
1339 =item C<$data-E<gt>{holdingbranch}>
1341 Branch Name of the item's holdingbranch
1343 =item C<$data-E<gt>{location}>
1345 Item's shelving location code
1347 =item C<$data-E<gt>{location_intranet}>
1349 The intranet description for the Shelving Location as set in authorised_values 'LOC'
1351 =item C<$data-E<gt>{location_opac}>
1353 The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC
1354 description is set.
1356 =item C<$data-E<gt>{itemcallnumber}>
1358 Item's itemcallnumber
1360 =item C<$data-E<gt>{cn_sort}>
1362 Item's call number normalized for sorting
1364 =back
1366 =cut
1368 sub GetItemsLocationInfo {
1369 my $biblionumber = shift;
1370 my @results;
1372 my $dbh = C4::Context->dbh;
1373 my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch,
1374 location, itemcallnumber, cn_sort
1375 FROM items, branches as a, branches as b
1376 WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode
1377 AND biblionumber = ?
1378 ORDER BY cn_sort ASC";
1379 my $sth = $dbh->prepare($query);
1380 $sth->execute($biblionumber);
1382 while ( my $data = $sth->fetchrow_hashref ) {
1383 $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1384 $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1385 push @results, $data;
1387 return @results;
1390 =head2 GetHostItemsInfo
1392 $hostiteminfo = GetHostItemsInfo($hostfield);
1393 Returns the iteminfo for items linked to records via a host field
1395 =cut
1397 sub GetHostItemsInfo {
1398 my ($record) = @_;
1399 my @returnitemsInfo;
1401 if (C4::Context->preference('marcflavour') eq 'MARC21' ||
1402 C4::Context->preference('marcflavour') eq 'NORMARC'){
1403 foreach my $hostfield ( $record->field('773') ) {
1404 my $hostbiblionumber = $hostfield->subfield("0");
1405 my $linkeditemnumber = $hostfield->subfield("9");
1406 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1407 foreach my $hostitemInfo (@hostitemInfos){
1408 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1409 push (@returnitemsInfo,$hostitemInfo);
1410 last;
1414 } elsif ( C4::Context->preference('marcflavour') eq 'UNIMARC'){
1415 foreach my $hostfield ( $record->field('461') ) {
1416 my $hostbiblionumber = $hostfield->subfield("0");
1417 my $linkeditemnumber = $hostfield->subfield("9");
1418 my @hostitemInfos = GetItemsInfo($hostbiblionumber);
1419 foreach my $hostitemInfo (@hostitemInfos){
1420 if ($hostitemInfo->{itemnumber} eq $linkeditemnumber){
1421 push (@returnitemsInfo,$hostitemInfo);
1422 last;
1427 return @returnitemsInfo;
1431 =head2 GetLastAcquisitions
1433 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'),
1434 'itemtypes' => ('BK','BD')}, 10);
1436 =cut
1438 sub GetLastAcquisitions {
1439 my ($data,$max) = @_;
1441 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1443 my $number_of_branches = @{$data->{branches}};
1444 my $number_of_itemtypes = @{$data->{itemtypes}};
1447 my @where = ('WHERE 1 ');
1448 $number_of_branches and push @where
1449 , 'AND holdingbranch IN ('
1450 , join(',', ('?') x $number_of_branches )
1451 , ')'
1454 $number_of_itemtypes and push @where
1455 , "AND $itemtype IN ("
1456 , join(',', ('?') x $number_of_itemtypes )
1457 , ')'
1460 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1461 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1462 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1463 @where
1464 GROUP BY biblio.biblionumber
1465 ORDER BY dateaccessioned DESC LIMIT $max";
1467 my $dbh = C4::Context->dbh;
1468 my $sth = $dbh->prepare($query);
1470 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1472 my @results;
1473 while( my $row = $sth->fetchrow_hashref){
1474 push @results, {date => $row->{dateaccessioned}
1475 , biblionumber => $row->{biblionumber}
1476 , title => $row->{title}};
1479 return @results;
1482 =head2 GetItemnumbersForBiblio
1484 my $itemnumbers = GetItemnumbersForBiblio($biblionumber);
1486 Given a single biblionumber, return an arrayref of all the corresponding itemnumbers
1488 =cut
1490 sub GetItemnumbersForBiblio {
1491 my $biblionumber = shift;
1492 my @items;
1493 my $dbh = C4::Context->dbh;
1494 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
1495 $sth->execute($biblionumber);
1496 while (my $result = $sth->fetchrow_hashref) {
1497 push @items, $result->{'itemnumber'};
1499 return \@items;
1502 =head2 get_itemnumbers_of
1504 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1506 Given a list of biblionumbers, return the list of corresponding itemnumbers
1507 for each biblionumber.
1509 Return a reference on a hash where keys are biblionumbers and values are
1510 references on array of itemnumbers.
1512 =cut
1514 sub get_itemnumbers_of {
1515 my @biblionumbers = @_;
1517 my $dbh = C4::Context->dbh;
1519 my $query = '
1520 SELECT itemnumber,
1521 biblionumber
1522 FROM items
1523 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1525 my $sth = $dbh->prepare($query);
1526 $sth->execute(@biblionumbers);
1528 my %itemnumbers_of;
1530 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1531 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1534 return \%itemnumbers_of;
1537 =head2 get_hostitemnumbers_of
1539 my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);
1541 Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields
1543 Return a reference on a hash where key is a biblionumber and values are
1544 references on array of itemnumbers.
1546 =cut
1549 sub get_hostitemnumbers_of {
1550 my ($biblionumber) = @_;
1551 my $marcrecord = GetMarcBiblio($biblionumber);
1552 my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1554 my $marcflavor = C4::Context->preference('marcflavour');
1555 if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1556 $tag='773';
1557 $biblio_s='0';
1558 $item_s='9';
1559 } elsif ($marcflavor eq 'UNIMARC') {
1560 $tag='461';
1561 $biblio_s='0';
1562 $item_s='9';
1565 foreach my $hostfield ( $marcrecord->field($tag) ) {
1566 my $hostbiblionumber = $hostfield->subfield($biblio_s);
1567 my $linkeditemnumber = $hostfield->subfield($item_s);
1568 my @itemnumbers;
1569 if (my $itemnumbers = get_itemnumbers_of($hostbiblionumber)->{$hostbiblionumber})
1571 @itemnumbers = @$itemnumbers;
1573 foreach my $itemnumber (@itemnumbers){
1574 if ($itemnumber eq $linkeditemnumber){
1575 push (@returnhostitemnumbers,$itemnumber);
1576 last;
1580 return @returnhostitemnumbers;
1584 =head2 GetItemnumberFromBarcode
1586 $result = GetItemnumberFromBarcode($barcode);
1588 =cut
1590 sub GetItemnumberFromBarcode {
1591 my ($barcode) = @_;
1592 my $dbh = C4::Context->dbh;
1594 my $rq =
1595 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1596 $rq->execute($barcode);
1597 my ($result) = $rq->fetchrow;
1598 return ($result);
1601 =head2 GetBarcodeFromItemnumber
1603 $result = GetBarcodeFromItemnumber($itemnumber);
1605 =cut
1607 sub GetBarcodeFromItemnumber {
1608 my ($itemnumber) = @_;
1609 my $dbh = C4::Context->dbh;
1611 my $rq =
1612 $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1613 $rq->execute($itemnumber);
1614 my ($result) = $rq->fetchrow;
1615 return ($result);
1618 =head2 GetHiddenItemnumbers
1620 my @itemnumbers_to_hide = GetHiddenItemnumbers(@items);
1622 Given a list of items it checks which should be hidden from the OPAC given
1623 the current configuration. Returns a list of itemnumbers corresponding to
1624 those that should be hidden.
1626 =cut
1628 sub GetHiddenItemnumbers {
1629 my (@items) = @_;
1630 my @resultitems;
1632 my $yaml = C4::Context->preference('OpacHiddenItems');
1633 return () if (! $yaml =~ /\S/ );
1634 $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1635 my $hidingrules;
1636 eval {
1637 $hidingrules = YAML::Load($yaml);
1639 if ($@) {
1640 warn "Unable to parse OpacHiddenItems syspref : $@";
1641 return ();
1643 my $dbh = C4::Context->dbh;
1645 # For each item
1646 foreach my $item (@items) {
1648 # We check each rule
1649 foreach my $field (keys %$hidingrules) {
1650 my $val;
1651 if (exists $item->{$field}) {
1652 $val = $item->{$field};
1654 else {
1655 my $query = "SELECT $field from items where itemnumber = ?";
1656 $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1658 $val = '' unless defined $val;
1660 # If the results matches the values in the yaml file
1661 if (any { $val eq $_ } @{$hidingrules->{$field}}) {
1663 # We add the itemnumber to the list
1664 push @resultitems, $item->{'itemnumber'};
1666 # If at least one rule matched for an item, no need to test the others
1667 last;
1671 return @resultitems;
1674 =head3 get_item_authorised_values
1676 find the types and values for all authorised values assigned to this item.
1678 parameters: itemnumber
1680 returns: a hashref malling the authorised value to the value set for this itemnumber
1682 $authorised_values = {
1683 'CCODE' => undef,
1684 'DAMAGED' => '0',
1685 'LOC' => '3',
1686 'LOST' => '0'
1687 'NOT_LOAN' => '0',
1688 'RESTRICTED' => undef,
1689 'STACK' => undef,
1690 'WITHDRAWN' => '0',
1691 'branches' => 'CPL',
1692 'cn_source' => undef,
1693 'itemtypes' => 'SER',
1696 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1698 =cut
1700 sub get_item_authorised_values {
1701 my $itemnumber = shift;
1703 # assume that these entries in the authorised_value table are item level.
1704 my $query = q(SELECT distinct authorised_value, kohafield
1705 FROM marc_subfield_structure
1706 WHERE kohafield like 'item%'
1707 AND authorised_value != '' );
1709 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1710 my $iteminfo = GetItem( $itemnumber );
1711 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1712 my $return;
1713 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1714 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1715 $field =~ s/^items\.//;
1716 if ( exists $iteminfo->{ $field } ) {
1717 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1720 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1721 return $return;
1724 =head3 get_authorised_value_images
1726 find a list of icons that are appropriate for display based on the
1727 authorised values for a biblio.
1729 parameters: listref of authorised values, such as comes from
1730 get_item_authorised_values or
1731 from C4::Biblio::get_biblio_authorised_values
1733 returns: listref of hashrefs for each image. Each hashref looks like this:
1735 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1736 label => '',
1737 category => '',
1738 value => '', }
1740 Notes: Currently, I put on the full path to the images on the staff
1741 side. This should either be configurable or not done at all. Since I
1742 have to deal with 'intranet' or 'opac' in
1743 get_biblio_authorised_values, perhaps I should be passing it in.
1745 =cut
1747 sub get_authorised_value_images {
1748 my $authorised_values = shift;
1750 my @imagelist;
1752 my $authorised_value_list = GetAuthorisedValues();
1753 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1754 foreach my $this_authorised_value ( @$authorised_value_list ) {
1755 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1756 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1757 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1758 if ( defined $this_authorised_value->{'imageurl'} ) {
1759 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1760 label => $this_authorised_value->{'lib'},
1761 category => $this_authorised_value->{'category'},
1762 value => $this_authorised_value->{'authorised_value'}, };
1767 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1768 return \@imagelist;
1772 =head1 LIMITED USE FUNCTIONS
1774 The following functions, while part of the public API,
1775 are not exported. This is generally because they are
1776 meant to be used by only one script for a specific
1777 purpose, and should not be used in any other context
1778 without careful thought.
1780 =cut
1782 =head2 GetMarcItem
1784 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1786 Returns MARC::Record of the item passed in parameter.
1787 This function is meant for use only in C<cataloguing/additem.pl>,
1788 where it is needed to support that script's MARC-like
1789 editor.
1791 =cut
1793 sub GetMarcItem {
1794 my ( $biblionumber, $itemnumber ) = @_;
1796 # GetMarcItem has been revised so that it does the following:
1797 # 1. Gets the item information from the items table.
1798 # 2. Converts it to a MARC field for storage in the bib record.
1800 # The previous behavior was:
1801 # 1. Get the bib record.
1802 # 2. Return the MARC tag corresponding to the item record.
1804 # The difference is that one treats the items row as authoritative,
1805 # while the other treats the MARC representation as authoritative
1806 # under certain circumstances.
1808 my $itemrecord = GetItem($itemnumber);
1810 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1811 # Also, don't emit a subfield if the underlying field is blank.
1814 return Item2Marc($itemrecord,$biblionumber);
1817 sub Item2Marc {
1818 my ($itemrecord,$biblionumber)=@_;
1819 my $mungeditem = {
1820 map {
1821 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1822 } keys %{ $itemrecord }
1824 my $itemmarc = TransformKohaToMarc($mungeditem);
1825 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1827 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1828 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1829 foreach my $field ($itemmarc->field($itemtag)){
1830 $field->add_subfields(@$unlinked_item_subfields);
1833 return $itemmarc;
1836 =head1 PRIVATE FUNCTIONS AND VARIABLES
1838 The following functions are not meant to be called
1839 directly, but are documented in order to explain
1840 the inner workings of C<C4::Items>.
1842 =cut
1844 =head2 %derived_columns
1846 This hash keeps track of item columns that
1847 are strictly derived from other columns in
1848 the item record and are not meant to be set
1849 independently.
1851 Each key in the hash should be the name of a
1852 column (as named by TransformMarcToKoha). Each
1853 value should be hashref whose keys are the
1854 columns on which the derived column depends. The
1855 hashref should also contain a 'BUILDER' key
1856 that is a reference to a sub that calculates
1857 the derived value.
1859 =cut
1861 my %derived_columns = (
1862 'items.cn_sort' => {
1863 'itemcallnumber' => 1,
1864 'items.cn_source' => 1,
1865 'BUILDER' => \&_calc_items_cn_sort,
1869 =head2 _set_derived_columns_for_add
1871 _set_derived_column_for_add($item);
1873 Given an item hash representing a new item to be added,
1874 calculate any derived columns. Currently the only
1875 such column is C<items.cn_sort>.
1877 =cut
1879 sub _set_derived_columns_for_add {
1880 my $item = shift;
1882 foreach my $column (keys %derived_columns) {
1883 my $builder = $derived_columns{$column}->{'BUILDER'};
1884 my $source_values = {};
1885 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1886 next if $source_column eq 'BUILDER';
1887 $source_values->{$source_column} = $item->{$source_column};
1889 $builder->($item, $source_values);
1893 =head2 _set_derived_columns_for_mod
1895 _set_derived_column_for_mod($item);
1897 Given an item hash representing a new item to be modified.
1898 calculate any derived columns. Currently the only
1899 such column is C<items.cn_sort>.
1901 This routine differs from C<_set_derived_columns_for_add>
1902 in that it needs to handle partial item records. In other
1903 words, the caller of C<ModItem> may have supplied only one
1904 or two columns to be changed, so this function needs to
1905 determine whether any of the columns to be changed affect
1906 any of the derived columns. Also, if a derived column
1907 depends on more than one column, but the caller is not
1908 changing all of then, this routine retrieves the unchanged
1909 values from the database in order to ensure a correct
1910 calculation.
1912 =cut
1914 sub _set_derived_columns_for_mod {
1915 my $item = shift;
1917 foreach my $column (keys %derived_columns) {
1918 my $builder = $derived_columns{$column}->{'BUILDER'};
1919 my $source_values = {};
1920 my %missing_sources = ();
1921 my $must_recalc = 0;
1922 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1923 next if $source_column eq 'BUILDER';
1924 if (exists $item->{$source_column}) {
1925 $must_recalc = 1;
1926 $source_values->{$source_column} = $item->{$source_column};
1927 } else {
1928 $missing_sources{$source_column} = 1;
1931 if ($must_recalc) {
1932 foreach my $source_column (keys %missing_sources) {
1933 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1935 $builder->($item, $source_values);
1940 =head2 _do_column_fixes_for_mod
1942 _do_column_fixes_for_mod($item);
1944 Given an item hashref containing one or more
1945 columns to modify, fix up certain values.
1946 Specifically, set to 0 any passed value
1947 of C<notforloan>, C<damaged>, C<itemlost>, or
1948 C<withdrawn> that is either undefined or
1949 contains the empty string.
1951 =cut
1953 sub _do_column_fixes_for_mod {
1954 my $item = shift;
1956 if (exists $item->{'notforloan'} and
1957 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1958 $item->{'notforloan'} = 0;
1960 if (exists $item->{'damaged'} and
1961 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1962 $item->{'damaged'} = 0;
1964 if (exists $item->{'itemlost'} and
1965 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1966 $item->{'itemlost'} = 0;
1968 if (exists $item->{'withdrawn'} and
1969 (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1970 $item->{'withdrawn'} = 0;
1972 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1973 $item->{'permanent_location'} = $item->{'location'};
1975 if (exists $item->{'timestamp'}) {
1976 delete $item->{'timestamp'};
1980 =head2 _get_single_item_column
1982 _get_single_item_column($column, $itemnumber);
1984 Retrieves the value of a single column from an C<items>
1985 row specified by C<$itemnumber>.
1987 =cut
1989 sub _get_single_item_column {
1990 my $column = shift;
1991 my $itemnumber = shift;
1993 my $dbh = C4::Context->dbh;
1994 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1995 $sth->execute($itemnumber);
1996 my ($value) = $sth->fetchrow();
1997 return $value;
2000 =head2 _calc_items_cn_sort
2002 _calc_items_cn_sort($item, $source_values);
2004 Helper routine to calculate C<items.cn_sort>.
2006 =cut
2008 sub _calc_items_cn_sort {
2009 my $item = shift;
2010 my $source_values = shift;
2012 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2015 =head2 _set_defaults_for_add
2017 _set_defaults_for_add($item_hash);
2019 Given an item hash representing an item to be added, set
2020 correct default values for columns whose default value
2021 is not handled by the DBMS. This includes the following
2022 columns:
2024 =over 2
2026 =item *
2028 C<items.dateaccessioned>
2030 =item *
2032 C<items.notforloan>
2034 =item *
2036 C<items.damaged>
2038 =item *
2040 C<items.itemlost>
2042 =item *
2044 C<items.withdrawn>
2046 =back
2048 =cut
2050 sub _set_defaults_for_add {
2051 my $item = shift;
2052 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
2053 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost withdrawn));
2056 =head2 _koha_new_item
2058 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
2060 Perform the actual insert into the C<items> table.
2062 =cut
2064 sub _koha_new_item {
2065 my ( $item, $barcode ) = @_;
2066 my $dbh=C4::Context->dbh;
2067 my $error;
2068 my $query =
2069 "INSERT INTO items SET
2070 biblionumber = ?,
2071 biblioitemnumber = ?,
2072 barcode = ?,
2073 dateaccessioned = ?,
2074 booksellerid = ?,
2075 homebranch = ?,
2076 price = ?,
2077 replacementprice = ?,
2078 replacementpricedate = ?,
2079 datelastborrowed = ?,
2080 datelastseen = ?,
2081 stack = ?,
2082 notforloan = ?,
2083 damaged = ?,
2084 itemlost = ?,
2085 withdrawn = ?,
2086 itemcallnumber = ?,
2087 coded_location_qualifier = ?,
2088 restricted = ?,
2089 itemnotes = ?,
2090 holdingbranch = ?,
2091 paidfor = ?,
2092 location = ?,
2093 permanent_location = ?,
2094 onloan = ?,
2095 issues = ?,
2096 renewals = ?,
2097 reserves = ?,
2098 cn_source = ?,
2099 cn_sort = ?,
2100 ccode = ?,
2101 itype = ?,
2102 materials = ?,
2103 uri = ?,
2104 enumchron = ?,
2105 more_subfields_xml = ?,
2106 copynumber = ?,
2107 stocknumber = ?
2109 my $sth = $dbh->prepare($query);
2110 my $today = C4::Dates->today('iso');
2111 $sth->execute(
2112 $item->{'biblionumber'},
2113 $item->{'biblioitemnumber'},
2114 $barcode,
2115 $item->{'dateaccessioned'},
2116 $item->{'booksellerid'},
2117 $item->{'homebranch'},
2118 $item->{'price'},
2119 $item->{'replacementprice'},
2120 $item->{'replacementpricedate'} || $today,
2121 $item->{datelastborrowed},
2122 $item->{datelastseen} || $today,
2123 $item->{stack},
2124 $item->{'notforloan'},
2125 $item->{'damaged'},
2126 $item->{'itemlost'},
2127 $item->{'withdrawn'},
2128 $item->{'itemcallnumber'},
2129 $item->{'coded_location_qualifier'},
2130 $item->{'restricted'},
2131 $item->{'itemnotes'},
2132 $item->{'holdingbranch'},
2133 $item->{'paidfor'},
2134 $item->{'location'},
2135 $item->{'permanent_location'},
2136 $item->{'onloan'},
2137 $item->{'issues'},
2138 $item->{'renewals'},
2139 $item->{'reserves'},
2140 $item->{'items.cn_source'},
2141 $item->{'items.cn_sort'},
2142 $item->{'ccode'},
2143 $item->{'itype'},
2144 $item->{'materials'},
2145 $item->{'uri'},
2146 $item->{'enumchron'},
2147 $item->{'more_subfields_xml'},
2148 $item->{'copynumber'},
2149 $item->{'stocknumber'},
2152 my $itemnumber;
2153 if ( defined $sth->errstr ) {
2154 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2156 else {
2157 $itemnumber = $dbh->{'mysql_insertid'};
2160 return ( $itemnumber, $error );
2163 =head2 MoveItemFromBiblio
2165 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2167 Moves an item from a biblio to another
2169 Returns undef if the move failed or the biblionumber of the destination record otherwise
2171 =cut
2173 sub MoveItemFromBiblio {
2174 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2175 my $dbh = C4::Context->dbh;
2176 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2177 $sth->execute( $tobiblio );
2178 my ( $tobiblioitem ) = $sth->fetchrow();
2179 $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2180 my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2181 if ($return == 1) {
2182 ModZebra( $tobiblio, "specialUpdate", "biblioserver" );
2183 ModZebra( $frombiblio, "specialUpdate", "biblioserver" );
2184 # Checking if the item we want to move is in an order
2185 require C4::Acquisition;
2186 my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
2187 if ($order) {
2188 # Replacing the biblionumber within the order if necessary
2189 $order->{'biblionumber'} = $tobiblio;
2190 C4::Acquisition::ModOrder($order);
2192 return $tobiblio;
2194 return;
2197 =head2 DelItemCheck
2199 DelItemCheck($dbh, $biblionumber, $itemnumber);
2201 Exported function (core API) for deleting an item record in Koha if there no current issue.
2203 =cut
2205 sub DelItemCheck {
2206 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2207 my $error;
2209 my $countanalytics=GetAnalyticsCount($itemnumber);
2212 # check that there is no issue on this item before deletion.
2213 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2214 $sth->execute($itemnumber);
2216 my $item = GetItem($itemnumber);
2217 my $onloan=$sth->fetchrow;
2219 if ($onloan){
2220 $error = "book_on_loan"
2222 elsif ( !( C4::Context->userenv->{flags} & 1 )
2223 and C4::Context->preference("IndependentBranches")
2224 and ( C4::Context->userenv->{branch} ne $item->{'homebranch'} ) )
2226 $error = "not_same_branch";
2228 else{
2229 # check it doesnt have a waiting reserve
2230 $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2231 $sth->execute($itemnumber);
2232 my $reserve=$sth->fetchrow;
2233 if ($reserve){
2234 $error = "book_reserved";
2235 } elsif ($countanalytics > 0){
2236 $error = "linked_analytics";
2237 } else {
2238 DelItem($dbh, $biblionumber, $itemnumber);
2239 return 1;
2242 return $error;
2245 =head2 _koha_modify_item
2247 my ($itemnumber,$error) =_koha_modify_item( $item );
2249 Perform the actual update of the C<items> row. Note that this
2250 routine accepts a hashref specifying the columns to update.
2252 =cut
2254 sub _koha_modify_item {
2255 my ( $item ) = @_;
2256 my $dbh=C4::Context->dbh;
2257 my $error;
2259 my $query = "UPDATE items SET ";
2260 my @bind;
2261 for my $key ( keys %$item ) {
2262 next if ( $key eq 'itemnumber' );
2263 $query.="$key=?,";
2264 push @bind, $item->{$key};
2266 $query =~ s/,$//;
2267 $query .= " WHERE itemnumber=?";
2268 push @bind, $item->{'itemnumber'};
2269 my $sth = C4::Context->dbh->prepare($query);
2270 $sth->execute(@bind);
2271 if ( C4::Context->dbh->errstr ) {
2272 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2273 warn $error;
2275 return ($item->{'itemnumber'},$error);
2278 =head2 _koha_delete_item
2280 _koha_delete_item( $dbh, $itemnum );
2282 Internal function to delete an item record from the koha tables
2284 =cut
2286 sub _koha_delete_item {
2287 my ( $dbh, $itemnum ) = @_;
2289 # save the deleted item to deleteditems table
2290 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2291 $sth->execute($itemnum);
2292 my $data = $sth->fetchrow_hashref();
2293 my $query = "INSERT INTO deleteditems SET ";
2294 my @bind = ();
2295 foreach my $key ( keys %$data ) {
2296 $query .= "$key = ?,";
2297 push( @bind, $data->{$key} );
2299 $query =~ s/\,$//;
2300 $sth = $dbh->prepare($query);
2301 $sth->execute(@bind);
2303 # delete from items table
2304 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2305 $sth->execute($itemnum);
2306 return;
2309 =head2 _marc_from_item_hash
2311 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2313 Given an item hash representing a complete item record,
2314 create a C<MARC::Record> object containing an embedded
2315 tag representing that item.
2317 The third, optional parameter C<$unlinked_item_subfields> is
2318 an arrayref of subfields (not mapped to C<items> fields per the
2319 framework) to be added to the MARC representation
2320 of the item.
2322 =cut
2324 sub _marc_from_item_hash {
2325 my $item = shift;
2326 my $frameworkcode = shift;
2327 my $unlinked_item_subfields;
2328 if (@_) {
2329 $unlinked_item_subfields = shift;
2332 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2333 # Also, don't emit a subfield if the underlying field is blank.
2334 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2335 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2336 : () } keys %{ $item } };
2338 my $item_marc = MARC::Record->new();
2339 foreach my $item_field ( keys %{$mungeditem} ) {
2340 my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2341 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2342 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2343 foreach my $value (@values){
2344 if ( my $field = $item_marc->field($tag) ) {
2345 $field->add_subfields( $subfield => $value );
2346 } else {
2347 my $add_subfields = [];
2348 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2349 $add_subfields = $unlinked_item_subfields;
2351 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2356 return $item_marc;
2359 =head2 _repack_item_errors
2361 Add an error message hash generated by C<CheckItemPreSave>
2362 to a list of errors.
2364 =cut
2366 sub _repack_item_errors {
2367 my $item_sequence_num = shift;
2368 my $item_ref = shift;
2369 my $error_ref = shift;
2371 my @repacked_errors = ();
2373 foreach my $error_code (sort keys %{ $error_ref }) {
2374 my $repacked_error = {};
2375 $repacked_error->{'item_sequence'} = $item_sequence_num;
2376 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2377 $repacked_error->{'error_code'} = $error_code;
2378 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2379 push @repacked_errors, $repacked_error;
2382 return @repacked_errors;
2385 =head2 _get_unlinked_item_subfields
2387 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2389 =cut
2391 sub _get_unlinked_item_subfields {
2392 my $original_item_marc = shift;
2393 my $frameworkcode = shift;
2395 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2397 # assume that this record has only one field, and that that
2398 # field contains only the item information
2399 my $subfields = [];
2400 my @fields = $original_item_marc->fields();
2401 if ($#fields > -1) {
2402 my $field = $fields[0];
2403 my $tag = $field->tag();
2404 foreach my $subfield ($field->subfields()) {
2405 if (defined $subfield->[1] and
2406 $subfield->[1] ne '' and
2407 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2408 push @$subfields, $subfield->[0] => $subfield->[1];
2412 return $subfields;
2415 =head2 _get_unlinked_subfields_xml
2417 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2419 =cut
2421 sub _get_unlinked_subfields_xml {
2422 my $unlinked_item_subfields = shift;
2424 my $xml;
2425 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2426 my $marc = MARC::Record->new();
2427 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2428 # used in the framework
2429 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2430 $marc->encoding("UTF-8");
2431 $xml = $marc->as_xml("USMARC");
2434 return $xml;
2437 =head2 _parse_unlinked_item_subfields_from_xml
2439 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2441 =cut
2443 sub _parse_unlinked_item_subfields_from_xml {
2444 my $xml = shift;
2445 require C4::Charset;
2446 return unless defined $xml and $xml ne "";
2447 my $marc = MARC::Record->new_from_xml(C4::Charset::StripNonXmlChars($xml),'UTF-8');
2448 my $unlinked_subfields = [];
2449 my @fields = $marc->fields();
2450 if ($#fields > -1) {
2451 foreach my $subfield ($fields[0]->subfields()) {
2452 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2455 return $unlinked_subfields;
2458 =head2 GetAnalyticsCount
2460 $count= &GetAnalyticsCount($itemnumber)
2462 counts Usage of itemnumber in Analytical bibliorecords.
2464 =cut
2466 sub GetAnalyticsCount {
2467 my ($itemnumber) = @_;
2468 require C4::Search;
2470 ### ZOOM search here
2471 my $query;
2472 $query= "hi=".$itemnumber;
2473 my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10);
2474 return ($result);
2477 =head2 GetItemHolds
2479 =over 4
2480 $holds = &GetItemHolds($biblionumber, $itemnumber);
2482 =back
2484 This function return the count of holds with $biblionumber and $itemnumber
2486 =cut
2488 sub GetItemHolds {
2489 my ($biblionumber, $itemnumber) = @_;
2490 my $holds;
2491 my $dbh = C4::Context->dbh;
2492 my $query = "SELECT count(*)
2493 FROM reserves
2494 WHERE biblionumber=? AND itemnumber=?";
2495 my $sth = $dbh->prepare($query);
2496 $sth->execute($biblionumber, $itemnumber);
2497 $holds = $sth->fetchrow;
2498 return $holds;
2501 # Return the list of the column names of items table
2502 sub _get_items_columns {
2503 my $dbh = C4::Context->dbh;
2504 my $sth = $dbh->column_info(undef, undef, 'items', '%');
2505 $sth->execute;
2506 my $results = $sth->fetchall_hashref('COLUMN_NAME');
2507 return keys %$results;
2510 =head2 SearchItems
2512 my $items = SearchItems($field, $value);
2514 SearchItems will search for items on a specific given field.
2515 For instance you can search all items with a specific stocknumber like this:
2517 my $items = SearchItems('stocknumber', $stocknumber);
2519 =cut
2521 sub SearchItems {
2522 my ($field, $value) = @_;
2524 my $dbh = C4::Context->dbh;
2525 my @columns = _get_items_columns;
2526 my $results = [];
2527 if(0 < grep /^$field$/, @columns) {
2528 my $query = "SELECT $field FROM items WHERE $field = ?";
2529 my $sth = $dbh->prepare( $query );
2530 $sth->execute( $value );
2531 $results = $sth->fetchall_arrayref({});
2533 return $results;
2537 =head1 OTHER FUNCTIONS
2539 =head2 _find_value
2541 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2543 Find the given $subfield in the given $tag in the given
2544 MARC::Record $record. If the subfield is found, returns
2545 the (indicators, value) pair; otherwise, (undef, undef) is
2546 returned.
2548 PROPOSITION :
2549 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2550 I suggest we export it from this module.
2552 =cut
2554 sub _find_value {
2555 my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2556 my @result;
2557 my $indicator;
2558 if ( $tagfield < 10 ) {
2559 if ( $record->field($tagfield) ) {
2560 push @result, $record->field($tagfield)->data();
2561 } else {
2562 push @result, "";
2564 } else {
2565 foreach my $field ( $record->field($tagfield) ) {
2566 my @subfields = $field->subfields();
2567 foreach my $subfield (@subfields) {
2568 if ( @$subfield[0] eq $insubfield ) {
2569 push @result, @$subfield[1];
2570 $indicator = $field->indicator(1) . $field->indicator(2);
2575 return ( $indicator, @result );
2579 =head2 PrepareItemrecordDisplay
2581 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2583 Returns a hash with all the fields for Display a given item data in a template
2585 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2587 =cut
2589 sub PrepareItemrecordDisplay {
2591 my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2593 my $dbh = C4::Context->dbh;
2594 $frameworkcode = &GetFrameworkCode($bibnum) if $bibnum;
2595 my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
2596 my $tagslib = &GetMarcStructure( 1, $frameworkcode );
2598 # return nothing if we don't have found an existing framework.
2599 return q{} unless $tagslib;
2600 my $itemrecord;
2601 if ($itemnum) {
2602 $itemrecord = C4::Items::GetMarcItem( $bibnum, $itemnum );
2604 my @loop_data;
2606 my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
2607 my $query = qq{
2608 SELECT authorised_value,lib FROM authorised_values
2610 $query .= qq{
2611 LEFT JOIN authorised_values_branches ON ( id = av_id )
2612 } if $branch_limit;
2613 $query .= qq{
2614 WHERE category = ?
2616 $query .= qq{ AND ( branchcode = ? OR branchcode IS NULL )} if $branch_limit;
2617 $query .= qq{ ORDER BY lib};
2618 my $authorised_values_sth = $dbh->prepare( $query );
2619 foreach my $tag ( sort keys %{$tagslib} ) {
2620 my $previous_tag = '';
2621 if ( $tag ne '' ) {
2623 # loop through each subfield
2624 my $cntsubf;
2625 foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2626 next if ( subfield_is_koha_internal_p($subfield) );
2627 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2628 my %subfield_data;
2629 $subfield_data{tag} = $tag;
2630 $subfield_data{subfield} = $subfield;
2631 $subfield_data{countsubfield} = $cntsubf++;
2632 $subfield_data{kohafield} = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2633 $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2635 # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2636 $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib};
2637 $subfield_data{mandatory} = $tagslib->{$tag}->{$subfield}->{mandatory};
2638 $subfield_data{repeatable} = $tagslib->{$tag}->{$subfield}->{repeatable};
2639 $subfield_data{hidden} = "display:none"
2640 if $tagslib->{$tag}->{$subfield}->{hidden};
2641 my ( $x, $defaultvalue );
2642 if ($itemrecord) {
2643 ( $x, $defaultvalue ) = _find_value( $tag, $subfield, $itemrecord );
2645 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue} unless $defaultvalue;
2646 if ( !defined $defaultvalue ) {
2647 $defaultvalue = q||;
2648 } else {
2649 $defaultvalue =~ s/"/&quot;/g;
2652 # search for itemcallnumber if applicable
2653 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2654 && C4::Context->preference('itemcallnumber') ) {
2655 my $CNtag = substr( C4::Context->preference('itemcallnumber'), 0, 3 );
2656 my $CNsubfield = substr( C4::Context->preference('itemcallnumber'), 3, 1 );
2657 if ( $itemrecord and my $field = $itemrecord->field($CNtag) ) {
2658 $defaultvalue = $field->subfield($CNsubfield);
2661 if ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.itemcallnumber'
2662 && $defaultvalues
2663 && $defaultvalues->{'callnumber'} ) {
2664 if( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ){
2665 # if the item record exists, only use default value if the item has no callnumber
2666 $defaultvalue = $defaultvalues->{callnumber};
2667 } elsif ( !$itemrecord and $defaultvalues ) {
2668 # if the item record *doesn't* exists, always use the default value
2669 $defaultvalue = $defaultvalues->{callnumber};
2672 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.homebranch' )
2673 && $defaultvalues
2674 && $defaultvalues->{'branchcode'} ) {
2675 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2676 $defaultvalue = $defaultvalues->{branchcode};
2679 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield} eq 'items.location' )
2680 && $defaultvalues
2681 && $defaultvalues->{'location'} ) {
2683 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2684 # if the item record exists, only use default value if the item has no locationr
2685 $defaultvalue = $defaultvalues->{location};
2686 } elsif ( !$itemrecord and $defaultvalues ) {
2687 # if the item record *doesn't* exists, always use the default value
2688 $defaultvalue = $defaultvalues->{location};
2691 if ( $tagslib->{$tag}->{$subfield}->{authorised_value} ) {
2692 my @authorised_values;
2693 my %authorised_lib;
2695 # builds list, depending on authorised value...
2696 #---- branch
2697 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2698 if ( ( C4::Context->preference("IndependentBranches") )
2699 && ( C4::Context->userenv->{flags} % 2 != 1 ) ) {
2700 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2701 $sth->execute( C4::Context->userenv->{branch} );
2702 push @authorised_values, ""
2703 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2704 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2705 push @authorised_values, $branchcode;
2706 $authorised_lib{$branchcode} = $branchname;
2708 } else {
2709 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2710 $sth->execute;
2711 push @authorised_values, ""
2712 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2713 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2714 push @authorised_values, $branchcode;
2715 $authorised_lib{$branchcode} = $branchname;
2719 $defaultvalue = C4::Context->userenv->{branch};
2720 if ( $defaultvalues and $defaultvalues->{branchcode} ) {
2721 $defaultvalue = $defaultvalues->{branchcode};
2724 #----- itemtypes
2725 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "itemtypes" ) {
2726 my $sth = $dbh->prepare( "SELECT itemtype,description FROM itemtypes ORDER BY description" );
2727 $sth->execute;
2728 push @authorised_values, ""
2729 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2730 while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) {
2731 push @authorised_values, $itemtype;
2732 $authorised_lib{$itemtype} = $description;
2734 #---- class_sources
2735 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value} eq "cn_source" ) {
2736 push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2738 my $class_sources = GetClassSources();
2739 my $default_source = C4::Context->preference("DefaultClassificationSource");
2741 foreach my $class_source (sort keys %$class_sources) {
2742 next unless $class_sources->{$class_source}->{'used'} or
2743 ($class_source eq $default_source);
2744 push @authorised_values, $class_source;
2745 $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2748 $defaultvalue = $default_source;
2750 #---- "true" authorised value
2751 } else {
2752 $authorised_values_sth->execute(
2753 $tagslib->{$tag}->{$subfield}->{authorised_value},
2754 $branch_limit ? $branch_limit : ()
2756 push @authorised_values, ""
2757 unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
2758 while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2759 push @authorised_values, $value;
2760 $authorised_lib{$value} = $lib;
2763 $subfield_data{marc_value} = CGI::scrolling_list(
2764 -name => 'field_value',
2765 -values => \@authorised_values,
2766 -default => "$defaultvalue",
2767 -labels => \%authorised_lib,
2768 -size => 1,
2769 -tabindex => '',
2770 -multiple => 0,
2772 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder} ) {
2773 # opening plugin
2774 my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $tagslib->{$tag}->{$subfield}->{'value_builder'};
2775 if (do $plugin) {
2776 my $extended_param = plugin_parameters( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2777 my ( $function_name, $javascript ) = plugin_javascript( $dbh, undef, $tagslib, $subfield_data{id}, undef );
2778 $subfield_data{random} = int(rand(1000000)); # why do we need 2 different randoms?
2779 $subfield_data{marc_value} = qq[<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255"
2780 onfocus="Focus$function_name($subfield_data{random}, '$subfield_data{id}');"
2781 onblur=" Blur$function_name($subfield_data{random}, '$subfield_data{id}');" />
2782 <a href="#" class="buttonDot" onclick="Clic$function_name('$subfield_data{id}'); return false;" title="Tag Editor">...</a>
2783 $javascript];
2784 } else {
2785 warn "Plugin Failed: $plugin";
2786 $subfield_data{marc_value} = qq(<input tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" />); # supply default input form
2789 elsif ( $tag eq '' ) { # it's an hidden field
2790 $subfield_data{marc_value} = qq(<input type="hidden" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2792 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ?
2793 $subfield_data{marc_value} = qq(<input type="text" tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255" value="$defaultvalue" />);
2795 elsif ( length($defaultvalue) > 100
2796 or (C4::Context->preference("marcflavour") eq "UNIMARC" and
2797 300 <= $tag && $tag < 400 && $subfield eq 'a' )
2798 or (C4::Context->preference("marcflavour") eq "MARC21" and
2799 500 <= $tag && $tag < 600 )
2801 # oversize field (textarea)
2802 $subfield_data{marc_value} = qq(<textarea tabindex="1" id="$subfield_data{id}" name="field_value" class="input_marceditor" size="67" maxlength="255">$defaultvalue</textarea>\n");
2803 } else {
2804 $subfield_data{marc_value} = "<input type=\"text\" name=\"field_value\" value=\"$defaultvalue\" size=\"50\" maxlength=\"255\" />";
2806 push( @loop_data, \%subfield_data );
2810 my $itemnumber;
2811 if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
2812 $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
2814 return {
2815 'itemtagfield' => $itemtagfield,
2816 'itemtagsubfield' => $itemtagsubfield,
2817 'itemnumber' => $itemnumber,
2818 'iteminformation' => \@loop_data