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
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 #use warnings; FIXME - Bug 2505
32 use List
::MoreUtils qw
/any/;
34 use DateTime
::Format
::MySQL
;
35 use Data
::Dumper
; # used as part of logging item record changes, not just for
36 # debugging; so please don't remove this
37 use Koha
::DateUtils qw
/dt_from_string/;
41 use Koha
::SearchEngine
;
42 use Koha
::SearchEngine
::Search
;
44 use vars
qw(@ISA @EXPORT);
49 @ISA = qw( Exporter );
72 GetItemsByBiblioitemnumber
76 GetItemnumbersForBiblio
78 get_hostitemnumbers_of
79 GetItemnumberFromBarcode
80 GetBarcodeFromItemnumber
95 PrepareItemrecordDisplay
102 C4::Items - item management functions
106 This module contains an API for manipulating item
107 records in Koha, and is used by cataloguing, circulation,
108 acquisitions, and serials management.
110 A Koha item record is stored in two places: the
111 items table and embedded in a MARC tag in the XML
112 version of the associated bib record in C<biblioitems.marcxml>.
113 This is done to allow the item information to be readily
114 indexed (e.g., by Zebra), but means that each item
115 modification transaction must keep the items table
116 and the MARC XML in sync at all times.
118 Consequently, all code that creates, modifies, or deletes
119 item records B<must> use an appropriate function from
120 C<C4::Items>. If no existing function is suitable, it is
121 better to add one to C<C4::Items> than to use add
122 one-off SQL statements to add or modify items.
124 The items table will be considered authoritative. In other
125 words, if there is ever a discrepancy between the items
126 table and the MARC XML, the items table should be considered
129 =head1 HISTORICAL NOTE
131 Most of the functions in C<C4::Items> were originally in
132 the C<C4::Biblio> module.
134 =head1 CORE EXPORTED FUNCTIONS
136 The following functions are meant for use by users
143 $item = GetItem($itemnumber,$barcode,$serial);
145 Return item information, for a given itemnumber or barcode.
146 The return value is a hashref mapping item column
147 names to values. If C<$serial> is true, include serial publication data.
152 my ($itemnumber,$barcode, $serial) = @_;
153 my $dbh = C4
::Context
->dbh;
157 my $sth = $dbh->prepare("
159 WHERE itemnumber = ?");
160 $sth->execute($itemnumber);
161 $data = $sth->fetchrow_hashref;
163 my $sth = $dbh->prepare("
167 $sth->execute($barcode);
168 $data = $sth->fetchrow_hashref;
171 return unless ( $data );
174 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
175 $ssth->execute($data->{'itemnumber'}) ;
176 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
178 #if we don't have an items.itype, use biblioitems.itemtype.
179 # FIXME this should respect the itypes systempreference
180 # if (C4::Context->preference('item-level_itypes')) {
181 if( ! $data->{'itype'} ) {
182 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
183 $sth->execute($data->{'biblionumber'});
184 ($data->{'itype'}) = $sth->fetchrow_array;
191 CartToShelf($itemnumber);
193 Set the current shelving location of the item record
194 to its stored permanent shelving location. This is
195 primarily used to indicate when an item whose current
196 location is a special processing ('PROC') or shelving cart
197 ('CART') location is back in the stacks.
202 my ( $itemnumber ) = @_;
204 unless ( $itemnumber ) {
205 croak
"FAILED CartToShelf() - no itemnumber supplied";
208 my $item = GetItem
($itemnumber);
209 if ( $item->{location
} eq 'CART' ) {
210 $item->{location
} = $item->{permanent_location
};
211 ModItem
($item, undef, $itemnumber);
217 ShelfToCart($itemnumber);
219 Set the current shelving location of the item
220 to shelving cart ('CART').
225 my ( $itemnumber ) = @_;
227 unless ( $itemnumber ) {
228 croak
"FAILED ShelfToCart() - no itemnumber supplied";
231 my $item = GetItem
($itemnumber);
232 $item->{'location'} = 'CART';
233 ModItem
($item, undef, $itemnumber);
236 =head2 AddItemFromMarc
238 my ($biblionumber, $biblioitemnumber, $itemnumber)
239 = AddItemFromMarc($source_item_marc, $biblionumber);
241 Given a MARC::Record object containing an embedded item
242 record and a biblionumber, create a new item record.
246 sub AddItemFromMarc
{
247 my ( $source_item_marc, $biblionumber ) = @_;
248 my $dbh = C4
::Context
->dbh;
250 # parse item hash from MARC
251 my $frameworkcode = GetFrameworkCode
( $biblionumber );
252 my ($itemtag,$itemsubfield)=GetMarcFromKohaField
("items.itemnumber",$frameworkcode);
254 my $localitemmarc=MARC
::Record
->new;
255 $localitemmarc->append_fields($source_item_marc->field($itemtag));
256 my $item = &TransformMarcToKoha
( $localitemmarc, $frameworkcode ,'items');
257 my $unlinked_item_subfields = _get_unlinked_item_subfields
($localitemmarc, $frameworkcode);
258 return AddItem
($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
263 my ($biblionumber, $biblioitemnumber, $itemnumber)
264 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
266 Given a hash containing item column names as keys,
267 create a new Koha item record.
269 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
270 do not need to be supplied for general use; they exist
271 simply to allow them to be picked up from AddItemFromMarc.
273 The final optional parameter, C<$unlinked_item_subfields>, contains
274 an arrayref containing subfields present in the original MARC
275 representation of the item (e.g., from the item editor) that are
276 not mapped to C<items> columns directly but should instead
277 be stored in C<items.more_subfields_xml> and included in
278 the biblio items tag for display and indexing.
284 my $biblionumber = shift;
286 my $dbh = @_ ?
shift : C4
::Context
->dbh;
287 my $frameworkcode = @_ ?
shift : GetFrameworkCode
( $biblionumber );
288 my $unlinked_item_subfields;
290 $unlinked_item_subfields = shift
293 # needs old biblionumber and biblioitemnumber
294 $item->{'biblionumber'} = $biblionumber;
295 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
296 $sth->execute( $item->{'biblionumber'} );
297 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
299 _set_defaults_for_add
($item);
300 _set_derived_columns_for_add
($item);
301 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml
($unlinked_item_subfields);
302 # FIXME - checks here
303 unless ( $item->{itype
} ) { # default to biblioitem.itemtype if no itype
304 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
305 $itype_sth->execute( $item->{'biblionumber'} );
306 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
309 my ( $itemnumber, $error ) = _koha_new_item
( $item, $item->{barcode
} );
310 $item->{'itemnumber'} = $itemnumber;
312 ModZebra
( $item->{biblionumber
}, "specialUpdate", "biblioserver" );
314 logaction
("CATALOGUING", "ADD", $itemnumber, "item") if C4
::Context
->preference("CataloguingLog");
316 return ($item->{biblionumber
}, $item->{biblioitemnumber
}, $itemnumber);
319 =head2 AddItemBatchFromMarc
321 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record,
322 $biblionumber, $biblioitemnumber, $frameworkcode);
324 Efficiently create item records from a MARC biblio record with
325 embedded item fields. This routine is suitable for batch jobs.
327 This API assumes that the bib record has already been
328 saved to the C<biblio> and C<biblioitems> tables. It does
329 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
330 are populated, but it will do so via a call to ModBibiloMarc.
332 The goal of this API is to have a similar effect to using AddBiblio
333 and AddItems in succession, but without inefficient repeated
334 parsing of the MARC XML bib record.
336 This function returns an arrayref of new itemsnumbers and an arrayref of item
337 errors encountered during the processing. Each entry in the errors
338 list is a hashref containing the following keys:
344 Sequence number of original item tag in the MARC record.
348 Item barcode, provide to assist in the construction of
349 useful error messages.
353 Code representing the error condition. Can be 'duplicate_barcode',
354 'invalid_homebranch', or 'invalid_holdingbranch'.
356 =item error_information
358 Additional information appropriate to the error condition.
364 sub AddItemBatchFromMarc
{
365 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
367 my @itemnumbers = ();
369 my $dbh = C4
::Context
->dbh;
371 # We modify the record, so lets work on a clone so we don't change the
373 $record = $record->clone();
374 # loop through the item tags and start creating items
375 my @bad_item_fields = ();
376 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField
("items.itemnumber",'');
377 my $item_sequence_num = 0;
378 ITEMFIELD
: foreach my $item_field ($record->field($itemtag)) {
379 $item_sequence_num++;
380 # we take the item field and stick it into a new
381 # MARC record -- this is required so far because (FIXME)
382 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
383 # and there is no TransformMarcFieldToKoha
384 my $temp_item_marc = MARC
::Record
->new();
385 $temp_item_marc->append_fields($item_field);
387 # add biblionumber and biblioitemnumber
388 my $item = TransformMarcToKoha
( $temp_item_marc, $frameworkcode, 'items' );
389 my $unlinked_item_subfields = _get_unlinked_item_subfields
($temp_item_marc, $frameworkcode);
390 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml
($unlinked_item_subfields);
391 $item->{'biblionumber'} = $biblionumber;
392 $item->{'biblioitemnumber'} = $biblioitemnumber;
394 # check for duplicate barcode
395 my %item_errors = CheckItemPreSave
($item);
397 push @errors, _repack_item_errors
($item_sequence_num, $item, \
%item_errors);
398 push @bad_item_fields, $item_field;
402 _set_defaults_for_add
($item);
403 _set_derived_columns_for_add
($item);
404 my ( $itemnumber, $error ) = _koha_new_item
( $item, $item->{barcode
} );
405 warn $error if $error;
406 push @itemnumbers, $itemnumber; # FIXME not checking error
407 $item->{'itemnumber'} = $itemnumber;
409 logaction
("CATALOGUING", "ADD", $itemnumber, "item") if C4
::Context
->preference("CataloguingLog");
411 my $new_item_marc = _marc_from_item_hash
($item, $frameworkcode, $unlinked_item_subfields);
412 $item_field->replace_with($new_item_marc->field($itemtag));
415 # remove any MARC item fields for rejected items
416 foreach my $item_field (@bad_item_fields) {
417 $record->delete_field($item_field);
420 # update the MARC biblio
421 # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
423 return (\
@itemnumbers, \
@errors);
426 =head2 ModItemFromMarc
428 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
430 This function updates an item record based on a supplied
431 C<MARC::Record> object containing an embedded item field.
432 This API is meant for the use of C<additem.pl>; for
433 other purposes, C<ModItem> should be used.
435 This function uses the hash %default_values_for_mod_from_marc,
436 which contains default values for item fields to
437 apply when modifying an item. This is needed because
438 if an item field's value is cleared, TransformMarcToKoha
439 does not include the column in the
440 hash that's passed to ModItem, which without
441 use of this hash makes it impossible to clear
442 an item field's value. See bug 2466.
444 Note that only columns that can be directly
445 changed from the cataloging and serials
446 item editors are included in this hash.
452 sub _build_default_values_for_mod_marc
{
453 my ($frameworkcode) = @_;
455 my $cache = Koha
::Cache
->get_instance();
456 my $cache_key = "default_value_for_mod_marc-$frameworkcode";
457 my $cached = $cache->get_from_cache($cache_key);
458 return $cached if $cached;
460 my $default_values = {
462 booksellerid
=> undef,
464 'items.cn_source' => undef,
465 coded_location_qualifier
=> undef,
469 holdingbranch
=> undef,
471 itemcallnumber
=> undef,
474 itemnotes_nonpublic
=> undef,
477 permanent_location
=> undef,
481 # paidfor => undef, # commented, see bug 12817
483 replacementprice
=> undef,
484 replacementpricedate
=> undef,
487 stocknumber
=> undef,
491 my %default_values_for_mod_from_marc;
492 while ( my ( $field, $default_value ) = each %$default_values ) {
493 my $kohafield = $field;
494 $kohafield =~ s
|^([^\
.]+)$|items
.$1|;
495 $default_values_for_mod_from_marc{$field} =
497 if C4
::Koha
::IsKohaFieldLinked
(
498 { kohafield
=> $kohafield, frameworkcode
=> $frameworkcode } );
501 $cache->set_in_cache($cache_key, \
%default_values_for_mod_from_marc);
502 return \
%default_values_for_mod_from_marc;
505 sub ModItemFromMarc
{
506 my $item_marc = shift;
507 my $biblionumber = shift;
508 my $itemnumber = shift;
510 my $dbh = C4
::Context
->dbh;
511 my $frameworkcode = GetFrameworkCode
($biblionumber);
512 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField
( "items.itemnumber", $frameworkcode );
514 my $localitemmarc = MARC
::Record
->new;
515 $localitemmarc->append_fields( $item_marc->field($itemtag) );
516 my $item = &TransformMarcToKoha
( $localitemmarc, $frameworkcode, 'items' );
517 my $default_values = _build_default_values_for_mod_marc
($frameworkcode);
518 foreach my $item_field ( keys %$default_values ) {
519 $item->{$item_field} = $default_values->{$item_field}
520 unless exists $item->{$item_field};
522 my $unlinked_item_subfields = _get_unlinked_item_subfields
( $localitemmarc, $frameworkcode );
524 ModItem
($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
530 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
532 Change one or more columns in an item record and update
533 the MARC representation of the item.
535 The first argument is a hashref mapping from item column
536 names to the new values. The second and third arguments
537 are the biblionumber and itemnumber, respectively.
539 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
540 an arrayref containing subfields present in the original MARC
541 representation of the item (e.g., from the item editor) that are
542 not mapped to C<items> columns directly but should instead
543 be stored in C<items.more_subfields_xml> and included in
544 the biblio items tag for display and indexing.
546 If one of the changed columns is used to calculate
547 the derived value of a column such as C<items.cn_sort>,
548 this routine will perform the necessary calculation
555 my $biblionumber = shift;
556 my $itemnumber = shift;
558 # if $biblionumber is undefined, get it from the current item
559 unless (defined $biblionumber) {
560 $biblionumber = _get_single_item_column
('biblionumber', $itemnumber);
563 my $dbh = @_ ?
shift : C4
::Context
->dbh;
564 my $frameworkcode = @_ ?
shift : GetFrameworkCode
( $biblionumber );
566 my $unlinked_item_subfields;
568 $unlinked_item_subfields = shift;
569 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml
($unlinked_item_subfields);
572 $item->{'itemnumber'} = $itemnumber or return;
574 my @fields = qw( itemlost withdrawn );
576 # Only call GetItem if we need to set an "on" date field
577 if ( $item->{itemlost
} || $item->{withdrawn
} ) {
578 my $pre_mod_item = GetItem
( $item->{'itemnumber'} );
579 for my $field (@fields) {
580 if ( defined( $item->{$field} )
581 and not $pre_mod_item->{$field}
582 and $item->{$field} )
584 $item->{ $field . '_on' } =
585 DateTime
::Format
::MySQL
->format_datetime( dt_from_string
() );
590 # If the field is defined but empty, we are removing and,
591 # and thus need to clear out the 'on' field as well
592 for my $field (@fields) {
593 if ( defined( $item->{$field} ) && !$item->{$field} ) {
594 $item->{ $field . '_on' } = undef;
599 _set_derived_columns_for_mod
($item);
600 _do_column_fixes_for_mod
($item);
603 # attempt to change itemnumber
604 # attempt to change biblionumber (if we want
605 # an API to relink an item to a different bib,
606 # it should be a separate function)
609 _koha_modify_item
($item);
611 # request that bib be reindexed so that searching on current
612 # item status is possible
613 ModZebra
( $biblionumber, "specialUpdate", "biblioserver" );
615 logaction
("CATALOGUING", "MODIFY", $itemnumber, "item ".Dumper
($item)) if C4
::Context
->preference("CataloguingLog");
618 =head2 ModItemTransfer
620 ModItemTransfer($itenumber, $frombranch, $tobranch);
622 Marks an item as being transferred from one branch
627 sub ModItemTransfer
{
628 my ( $itemnumber, $frombranch, $tobranch ) = @_;
630 my $dbh = C4
::Context
->dbh;
632 # Remove the 'shelving cart' location status if it is being used.
633 CartToShelf
( $itemnumber ) if ( C4
::Context
->preference("ReturnToShelvingCart") );
635 #new entry in branchtransfers....
636 my $sth = $dbh->prepare(
637 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
638 VALUES (?, ?, NOW(), ?)");
639 $sth->execute($itemnumber, $frombranch, $tobranch);
641 ModItem
({ holdingbranch
=> $tobranch }, undef, $itemnumber);
642 ModDateLastSeen
($itemnumber);
646 =head2 ModDateLastSeen
648 ModDateLastSeen($itemnum);
650 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
651 C<$itemnum> is the item number
655 sub ModDateLastSeen
{
656 my ($itemnumber) = @_;
658 my $today = output_pref
({ dt
=> dt_from_string
, dateformat
=> 'iso', dateonly
=> 1 });
659 ModItem
({ itemlost
=> 0, datelastseen
=> $today }, undef, $itemnumber);
664 DelItem({ itemnumber => $itemnumber, [ biblionumber => $biblionumber ] } );
666 Exported function (core API) for deleting an item record in Koha.
673 my $itemnumber = $params->{itemnumber
};
674 my $biblionumber = $params->{biblionumber
};
676 unless ($biblionumber) {
677 $biblionumber = C4
::Biblio
::GetBiblionumberFromItemnumber
($itemnumber);
680 # If there is no biblionumber for the given itemnumber, there is nothing to delete
681 return 0 unless $biblionumber;
683 # FIXME check the item has no current issues
684 my $deleted = _koha_delete_item
( $itemnumber );
686 # get the MARC record
687 my $record = GetMarcBiblio
($biblionumber);
688 ModZebra
( $biblionumber, "specialUpdate", "biblioserver" );
690 #search item field code
691 logaction
("CATALOGUING", "DELETE", $itemnumber, "item") if C4
::Context
->preference("CataloguingLog");
695 =head2 CheckItemPreSave
697 my $item_ref = TransformMarcToKoha($marc, 'items');
699 my %errors = CheckItemPreSave($item_ref);
700 if (exists $errors{'duplicate_barcode'}) {
701 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
702 } elsif (exists $errors{'invalid_homebranch'}) {
703 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
704 } elsif (exists $errors{'invalid_holdingbranch'}) {
705 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
710 Given a hashref containing item fields, determine if it can be
711 inserted or updated in the database. Specifically, checks for
712 database integrity issues, and returns a hash containing any
713 of the following keys, if applicable.
717 =item duplicate_barcode
719 Barcode, if it duplicates one already found in the database.
721 =item invalid_homebranch
723 Home branch, if not defined in branches table.
725 =item invalid_holdingbranch
727 Holding branch, if not defined in branches table.
731 This function does NOT implement any policy-related checks,
732 e.g., whether current operator is allowed to save an
733 item that has a given branch code.
737 sub CheckItemPreSave
{
738 my $item_ref = shift;
743 # check for duplicate barcode
744 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
745 my $existing_itemnumber = GetItemnumberFromBarcode
($item_ref->{'barcode'});
746 if ($existing_itemnumber) {
747 if (!exists $item_ref->{'itemnumber'} # new item
748 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
749 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
754 # check for valid home branch
755 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
756 my $branch_name = C4
::Branch
::GetBranchName
($item_ref->{'homebranch'});
757 unless (defined $branch_name) {
758 # relies on fact that branches.branchname is a non-NULL column,
759 # so GetBranchName returns undef only if branch does not exist
760 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
764 # check for valid holding branch
765 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
766 my $branch_name = C4
::Branch
::GetBranchName
($item_ref->{'holdingbranch'});
767 unless (defined $branch_name) {
768 # relies on fact that branches.branchname is a non-NULL column,
769 # so GetBranchName returns undef only if branch does not exist
770 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
778 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
780 The following functions provide various ways of
781 getting an item record, a set of item records, or
782 lists of authorized values for certain item fields.
784 Some of the functions in this group are candidates
785 for refactoring -- for example, some of the code
786 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
787 has copy-and-paste work.
793 $itemstatushash = GetItemStatus($fwkcode);
795 Returns a list of valid values for the
796 C<items.notforloan> field.
798 NOTE: does B<not> return an individual item's
801 Can be MARC dependent.
803 But basically could be can be loan or not
804 Create a status selector with the following code
806 =head3 in PERL SCRIPT
808 my $itemstatushash = getitemstatus;
810 foreach my $thisstatus (keys %$itemstatushash) {
811 my %row =(value => $thisstatus,
812 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
814 push @itemstatusloop, \%row;
816 $template->param(statusloop=>\@itemstatusloop);
820 <select name="statusloop" id="statusloop">
821 <option value="">Default</option>
822 [% FOREACH statusloo IN statusloop %]
823 [% IF ( statusloo.selected ) %]
824 <option value="[% statusloo.value %]" selected="selected">[% statusloo.statusname %]</option>
826 <option value="[% statusloo.value %]">[% statusloo.statusname %]</option>
835 # returns a reference to a hash of references to status...
838 my $dbh = C4
::Context
->dbh;
840 $fwk = '' unless ($fwk);
841 my ( $tag, $subfield ) =
842 GetMarcFromKohaField
( "items.notforloan", $fwk );
843 if ( $tag and $subfield ) {
846 "SELECT authorised_value
847 FROM marc_subfield_structure
853 $sth->execute( $tag, $subfield, $fwk );
854 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
857 "SELECT authorised_value,lib
858 FROM authorised_values
863 $authvalsth->execute($authorisedvaluecat);
864 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
865 $itemstatus{$authorisedvalue} = $lib;
879 $itemstatus{"1"} = "Not For Loan";
883 =head2 GetItemLocation
885 $itemlochash = GetItemLocation($fwk);
887 Returns a list of valid values for the
888 C<items.location> field.
890 NOTE: does B<not> return an individual item's
893 where fwk stands for an optional framework code.
894 Create a location selector with the following code
896 =head3 in PERL SCRIPT
898 my $itemlochash = getitemlocation;
900 foreach my $thisloc (keys %$itemlochash) {
901 my $selected = 1 if $thisbranch eq $branch;
902 my %row =(locval => $thisloc,
903 selected => $selected,
904 locname => $itemlochash->{$thisloc},
906 push @itemlocloop, \%row;
908 $template->param(itemlocationloop => \@itemlocloop);
912 <select name="location">
913 <option value="">Default</option>
914 <!-- TMPL_LOOP name="itemlocationloop" -->
915 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
921 sub GetItemLocation
{
923 # returns a reference to a hash of references to location...
926 my $dbh = C4
::Context
->dbh;
928 $fwk = '' unless ($fwk);
929 my ( $tag, $subfield ) =
930 GetMarcFromKohaField
( "items.location", $fwk );
931 if ( $tag and $subfield ) {
934 "SELECT authorised_value
935 FROM marc_subfield_structure
940 $sth->execute( $tag, $subfield, $fwk );
941 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
944 "SELECT authorised_value,lib
945 FROM authorised_values
949 $authvalsth->execute($authorisedvaluecat);
950 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
951 $itemlocation{$authorisedvalue} = $lib;
953 return \
%itemlocation;
965 $itemlocation{"1"} = "Not For Loan";
966 return \
%itemlocation;
971 $items = GetLostItems( $where );
973 This function gets a list of lost items.
979 C<$where> is a hashref. it containts a field of the items table as key
980 and the value to match as value. For example:
982 { barcode => 'abc123',
983 homebranch => 'CPL', }
987 C<$items> is a reference to an array full of hashrefs with columns
988 from the "items" table as keys.
990 =item usage in the perl script:
992 my $where = { barcode => '0001548' };
993 my $items = GetLostItems( $where );
994 $template->param( itemsloop => $items );
1001 # Getting input args.
1003 my $dbh = C4
::Context
->dbh;
1006 SELECT title, author, lib, itemlost, authorised_value, barcode, datelastseen, price, replacementprice, homebranch,
1007 itype, itemtype, holdingbranch, location, itemnotes, items.biblionumber as biblionumber, itemcallnumber
1009 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
1010 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
1011 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
1013 authorised_values.category = 'LOST'
1014 AND itemlost IS NOT NULL
1017 my @query_parameters;
1018 foreach my $key (keys %$where) {
1019 $query .= " AND $key LIKE ?";
1020 push @query_parameters, "%$where->{$key}%";
1023 my $sth = $dbh->prepare($query);
1024 $sth->execute( @query_parameters );
1026 while ( my $row = $sth->fetchrow_hashref ){
1032 =head2 GetItemsForInventory
1034 ($itemlist, $iTotalRecords) = GetItemsForInventory( {
1035 minlocation => $minlocation,
1036 maxlocation => $maxlocation,
1037 location => $location,
1038 itemtype => $itemtype,
1039 ignoreissued => $ignoreissued,
1040 datelastseen => $datelastseen,
1041 branchcode => $branchcode,
1045 statushash => $statushash,
1046 interface => $interface,
1049 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1051 The sub returns a reference to a list of hashes, each containing
1052 itemnumber, author, title, barcode, item callnumber, and date last
1053 seen. It is ordered by callnumber then title.
1055 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
1056 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1057 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1058 $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.
1060 $iTotalRecords is the number of rows that would have been returned without the $offset, $size limit clause
1064 sub GetItemsForInventory
{
1065 my ( $parameters ) = @_;
1066 my $minlocation = $parameters->{'minlocation'} // '';
1067 my $maxlocation = $parameters->{'maxlocation'} // '';
1068 my $location = $parameters->{'location'} // '';
1069 my $itemtype = $parameters->{'itemtype'} // '';
1070 my $ignoreissued = $parameters->{'ignoreissued'} // '';
1071 my $datelastseen = $parameters->{'datelastseen'} // '';
1072 my $branchcode = $parameters->{'branchcode'} // '';
1073 my $branch = $parameters->{'branch'} // '';
1074 my $offset = $parameters->{'offset'} // '';
1075 my $size = $parameters->{'size'} // '';
1076 my $statushash = $parameters->{'statushash'} // '';
1077 my $interface = $parameters->{'interface'} // '';
1079 my $dbh = C4
::Context
->dbh;
1080 my ( @bind_params, @where_strings );
1082 my $select_columns = q{
1083 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, biblio.frameworkcode, datelastseen, homebranch, location, notforloan, damaged, itemlost, withdrawn, stocknumber
1085 my $select_count = q{SELECT COUNT(*)};
1088 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1089 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1092 for my $authvfield (keys %$statushash){
1093 if ( scalar @
{$statushash->{$authvfield}} > 0 ){
1094 my $joinedvals = join ',', @
{$statushash->{$authvfield}};
1095 push @where_strings, "$authvfield in (" . $joinedvals . ")";
1101 push @where_strings, 'itemcallnumber >= ?';
1102 push @bind_params, $minlocation;
1106 push @where_strings, 'itemcallnumber <= ?';
1107 push @bind_params, $maxlocation;
1110 if ($datelastseen) {
1111 $datelastseen = output_pref
({ str
=> $datelastseen, dateformat
=> 'iso', dateonly
=> 1 });
1112 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1113 push @bind_params, $datelastseen;
1117 push @where_strings, 'items.location = ?';
1118 push @bind_params, $location;
1121 if ( $branchcode ) {
1122 if($branch eq "homebranch"){
1123 push @where_strings, 'items.homebranch = ?';
1125 push @where_strings, 'items.holdingbranch = ?';
1127 push @bind_params, $branchcode;
1131 push @where_strings, 'biblioitems.itemtype = ?';
1132 push @bind_params, $itemtype;
1135 if ( $ignoreissued) {
1136 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1137 push @where_strings, 'issues.date_due IS NULL';
1140 if ( @where_strings ) {
1142 $query .= join ' AND ', @where_strings;
1144 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1145 my $count_query = $select_count . $query;
1146 $query .= " LIMIT $offset, $size" if ($offset and $size);
1147 $query = $select_columns . $query;
1148 my $sth = $dbh->prepare($query);
1149 $sth->execute( @bind_params );
1152 my $tmpresults = $sth->fetchall_arrayref({});
1153 $sth = $dbh->prepare( $count_query );
1154 $sth->execute( @bind_params );
1155 my ($iTotalRecords) = $sth->fetchrow_array();
1157 my $avmapping = C4
::Koha
::GetKohaAuthorisedValuesMapping
( {
1158 interface
=> $interface
1160 foreach my $row (@
$tmpresults) {
1163 foreach (keys %$row) {
1164 if (defined($avmapping->{"items.$_,".$row->{'frameworkcode'}.",".$row->{$_}})) {
1165 $row->{$_} = $avmapping->{"items.$_,".$row->{'frameworkcode'}.",".$row->{$_}};
1168 push @results, $row;
1171 return (\
@results, $iTotalRecords);
1174 =head2 GetItemsCount
1176 $count = &GetItemsCount( $biblionumber);
1178 This function return count of item with $biblionumber
1183 my ( $biblionumber ) = @_;
1184 my $dbh = C4
::Context
->dbh;
1185 my $query = "SELECT count(*)
1187 WHERE biblionumber=?";
1188 my $sth = $dbh->prepare($query);
1189 $sth->execute($biblionumber);
1190 my $count = $sth->fetchrow;
1194 =head2 GetItemInfosOf
1196 GetItemInfosOf(@itemnumbers);
1200 sub GetItemInfosOf
{
1201 my @itemnumbers = @_;
1203 my $itemnumber_values = @itemnumbers ?
join( ',', @itemnumbers ) : "''";
1208 WHERE itemnumber IN ($itemnumber_values)
1210 return get_infos_of
( $query, 'itemnumber' );
1213 =head2 GetItemsByBiblioitemnumber
1215 GetItemsByBiblioitemnumber($biblioitemnumber);
1217 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1218 Called by C<C4::XISBN>
1222 sub GetItemsByBiblioitemnumber
{
1223 my ( $bibitem ) = @_;
1224 my $dbh = C4
::Context
->dbh;
1225 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1226 # Get all items attached to a biblioitem
1229 $sth->execute($bibitem) || die $sth->errstr;
1230 while ( my $data = $sth->fetchrow_hashref ) {
1231 # Foreach item, get circulation information
1232 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1233 WHERE itemnumber = ?
1234 AND issues.borrowernumber = borrowers.borrowernumber"
1236 $sth2->execute( $data->{'itemnumber'} );
1237 if ( my $data2 = $sth2->fetchrow_hashref ) {
1238 # if item is out, set the due date and who it is out too
1239 $data->{'date_due'} = $data2->{'date_due'};
1240 $data->{'cardnumber'} = $data2->{'cardnumber'};
1241 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1244 # set date_due to blank, so in the template we check itemlost, and withdrawn
1245 $data->{'date_due'} = '';
1247 # Find the last 3 people who borrowed this item.
1248 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1249 AND old_issues.borrowernumber = borrowers.borrowernumber
1250 ORDER BY returndate desc,timestamp desc LIMIT 3";
1251 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1252 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1254 while ( my $data2 = $sth2->fetchrow_hashref ) {
1255 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1256 $data->{"card$i2"} = $data2->{'cardnumber'};
1257 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1260 push(@results,$data);
1267 @results = GetItemsInfo($biblionumber);
1269 Returns information about items with the given biblionumber.
1271 C<GetItemsInfo> returns a list of references-to-hash. Each element
1272 contains a number of keys. Most of them are attributes from the
1273 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1274 Koha database. Other keys include:
1278 =item C<$data-E<gt>{branchname}>
1280 The name (not the code) of the branch to which the book belongs.
1282 =item C<$data-E<gt>{datelastseen}>
1284 This is simply C<items.datelastseen>, except that while the date is
1285 stored in YYYY-MM-DD format in the database, here it is converted to
1286 DD/MM/YYYY format. A NULL date is returned as C<//>.
1288 =item C<$data-E<gt>{datedue}>
1290 =item C<$data-E<gt>{class}>
1292 This is the concatenation of C<biblioitems.classification>, the book's
1293 Dewey code, and C<biblioitems.subclass>.
1295 =item C<$data-E<gt>{ocount}>
1297 I think this is the number of copies of the book available.
1299 =item C<$data-E<gt>{order}>
1301 If this is set, it is set to C<One Order>.
1308 my ( $biblionumber ) = @_;
1309 my $dbh = C4
::Context
->dbh;
1310 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1311 require C4
::Languages
;
1312 my $language = C4
::Languages
::getlanguage
();
1318 biblioitems.itemtype,
1321 biblioitems.publicationyear,
1322 biblioitems.publishercode,
1323 biblioitems.volumedate,
1324 biblioitems.volumedesc,
1327 items.notforloan as itemnotforloan,
1328 issues.borrowernumber,
1329 issues.date_due as datedue,
1330 issues.onsite_checkout,
1331 borrowers.cardnumber,
1333 borrowers.firstname,
1334 borrowers.branchcode as bcode,
1336 serial.publisheddate,
1337 itemtypes.description,
1338 COALESCE( localization.translation, itemtypes.description ) AS translated_description,
1339 itemtypes.notforloan as notforloan_per_itemtype,
1342 holding.opac_info as holding_branch_opac_info,
1343 home.opac_info as home_branch_opac_info
1347 LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1348 LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1349 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1350 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1351 LEFT JOIN issues USING (itemnumber)
1352 LEFT JOIN borrowers USING (borrowernumber)
1353 LEFT JOIN serialitems USING (itemnumber)
1354 LEFT JOIN serial USING (serialid)
1355 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1356 . (C4
::Context
->preference('item-level_itypes') ?
'items.itype' : 'biblioitems.itemtype');
1358 LEFT JOIN localization ON itemtypes
.itemtype
= localization
.code
1359 AND localization
.entity
= 'itemtypes'
1360 AND localization
.lang
= ?
1363 $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1364 my $sth = $dbh->prepare($query);
1365 $sth->execute($language, $biblionumber);
1370 my $userenv = C4
::Context
->userenv;
1371 my $want_not_same_branch = C4
::Context
->preference("IndependentBranches") && !C4
::Context
->IsSuperLibrarian();
1372 while ( my $data = $sth->fetchrow_hashref ) {
1373 if ( $data->{borrowernumber
} && $want_not_same_branch) {
1374 $data->{'NOTSAMEBRANCH'} = $data->{'bcode'} ne $userenv->{branch
};
1377 $serial ||= $data->{'serial'};
1379 # get notforloan complete status if applicable
1380 if ( my $code = C4
::Koha
::GetAuthValCode
( 'items.notforloan', $data->{frameworkcode
} ) ) {
1381 $data->{notforloanvalue
} = C4
::Koha
::GetKohaAuthorisedValueLib
( $code, $data->{itemnotforloan
} );
1382 $data->{notforloanvalueopac
} = C4
::Koha
::GetKohaAuthorisedValueLib
( $code, $data->{itemnotforloan
}, 1 );
1385 # get restricted status and description if applicable
1386 if ( my $code = C4
::Koha
::GetAuthValCode
( 'items.restricted', $data->{frameworkcode
} ) ) {
1387 $data->{restrictedopac
} = C4
::Koha
::GetKohaAuthorisedValueLib
( $code, $data->{restricted
}, 1 );
1388 $data->{restricted
} = C4
::Koha
::GetKohaAuthorisedValueLib
( $code, $data->{restricted
} );
1391 # my stack procedures
1392 if ( my $code = C4
::Koha
::GetAuthValCode
( 'items.stack', $data->{frameworkcode
} ) ) {
1393 $data->{stack
} = C4
::Koha
::GetKohaAuthorisedValueLib
( $code, $data->{stack
} );
1396 # Find the last 3 people who borrowed this item.
1397 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1398 WHERE itemnumber = ?
1399 AND old_issues.borrowernumber = borrowers.borrowernumber
1400 ORDER BY returndate DESC
1402 $sth2->execute($data->{'itemnumber'});
1404 while (my $data2 = $sth2->fetchrow_hashref()) {
1405 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1406 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1407 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1411 $results[$i] = $data;
1416 ?
sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results
1420 =head2 GetItemsLocationInfo
1422 my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1424 Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1426 C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1430 =item C<$data-E<gt>{homebranch}>
1432 Branch Name of the item's homebranch
1434 =item C<$data-E<gt>{holdingbranch}>
1436 Branch Name of the item's holdingbranch
1438 =item C<$data-E<gt>{location}>
1440 Item's shelving location code
1442 =item C<$data-E<gt>{location_intranet}>
1444 The intranet description for the Shelving Location as set in authorised_values 'LOC'
1446 =item C<$data-E<gt>{location_opac}>
1448 The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC
1451 =item C<$data-E<gt>{itemcallnumber}>
1453 Item's itemcallnumber
1455 =item C<$data-E<gt>{cn_sort}>
1457 Item's call number normalized for sorting
1463 sub GetItemsLocationInfo
{
1464 my $biblionumber = shift;
1467 my $dbh = C4
::Context
->dbh;
1468 my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch,
1469 location, itemcallnumber, cn_sort
1470 FROM items, branches as a, branches as b
1471 WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode
1472 AND biblionumber = ?
1473 ORDER BY cn_sort ASC";
1474 my $sth = $dbh->prepare($query);
1475 $sth->execute($biblionumber);
1477 while ( my $data = $sth->fetchrow_hashref ) {
1478 $data->{location_intranet
} = GetKohaAuthorisedValueLib
('LOC', $data->{location
});
1479 $data->{location_opac
}= GetKohaAuthorisedValueLib
('LOC', $data->{location
}, 1);
1480 push @results, $data;
1485 =head2 GetHostItemsInfo
1487 $hostiteminfo = GetHostItemsInfo($hostfield);
1488 Returns the iteminfo for items linked to records via a host field
1492 sub GetHostItemsInfo
{
1494 my @returnitemsInfo;
1496 if (C4
::Context
->preference('marcflavour') eq 'MARC21' ||
1497 C4
::Context
->preference('marcflavour') eq 'NORMARC'){
1498 foreach my $hostfield ( $record->field('773') ) {
1499 my $hostbiblionumber = $hostfield->subfield("0");
1500 my $linkeditemnumber = $hostfield->subfield("9");
1501 my @hostitemInfos = GetItemsInfo
($hostbiblionumber);
1502 foreach my $hostitemInfo (@hostitemInfos){
1503 if ($hostitemInfo->{itemnumber
} eq $linkeditemnumber){
1504 push (@returnitemsInfo,$hostitemInfo);
1509 } elsif ( C4
::Context
->preference('marcflavour') eq 'UNIMARC'){
1510 foreach my $hostfield ( $record->field('461') ) {
1511 my $hostbiblionumber = $hostfield->subfield("0");
1512 my $linkeditemnumber = $hostfield->subfield("9");
1513 my @hostitemInfos = GetItemsInfo
($hostbiblionumber);
1514 foreach my $hostitemInfo (@hostitemInfos){
1515 if ($hostitemInfo->{itemnumber
} eq $linkeditemnumber){
1516 push (@returnitemsInfo,$hostitemInfo);
1522 return @returnitemsInfo;
1526 =head2 GetLastAcquisitions
1528 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'),
1529 'itemtypes' => ('BK','BD')}, 10);
1533 sub GetLastAcquisitions
{
1534 my ($data,$max) = @_;
1536 my $itemtype = C4
::Context
->preference('item-level_itypes') ?
'itype' : 'itemtype';
1538 my $number_of_branches = @
{$data->{branches
}};
1539 my $number_of_itemtypes = @
{$data->{itemtypes
}};
1542 my @where = ('WHERE 1 ');
1543 $number_of_branches and push @where
1544 , 'AND holdingbranch IN ('
1545 , join(',', ('?') x
$number_of_branches )
1549 $number_of_itemtypes and push @where
1550 , "AND $itemtype IN ("
1551 , join(',', ('?') x
$number_of_itemtypes )
1555 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1556 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1557 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1559 GROUP BY biblio.biblionumber
1560 ORDER BY dateaccessioned DESC LIMIT $max";
1562 my $dbh = C4
::Context
->dbh;
1563 my $sth = $dbh->prepare($query);
1565 $sth->execute((@
{$data->{branches
}}, @
{$data->{itemtypes
}}));
1568 while( my $row = $sth->fetchrow_hashref){
1569 push @results, {date
=> $row->{dateaccessioned
}
1570 , biblionumber
=> $row->{biblionumber
}
1571 , title
=> $row->{title
}};
1577 =head2 GetItemnumbersForBiblio
1579 my $itemnumbers = GetItemnumbersForBiblio($biblionumber);
1581 Given a single biblionumber, return an arrayref of all the corresponding itemnumbers
1585 sub GetItemnumbersForBiblio
{
1586 my $biblionumber = shift;
1588 my $dbh = C4
::Context
->dbh;
1589 my $sth = $dbh->prepare("SELECT itemnumber FROM items WHERE biblionumber = ?");
1590 $sth->execute($biblionumber);
1591 while (my $result = $sth->fetchrow_hashref) {
1592 push @items, $result->{'itemnumber'};
1597 =head2 get_itemnumbers_of
1599 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1601 Given a list of biblionumbers, return the list of corresponding itemnumbers
1602 for each biblionumber.
1604 Return a reference on a hash where keys are biblionumbers and values are
1605 references on array of itemnumbers.
1609 sub get_itemnumbers_of
{
1610 my @biblionumbers = @_;
1612 my $dbh = C4
::Context
->dbh;
1618 WHERE biblionumber IN (?' . ( ',?' x
scalar @biblionumbers - 1 ) . ')
1620 my $sth = $dbh->prepare($query);
1621 $sth->execute(@biblionumbers);
1625 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1626 push @
{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1629 return \
%itemnumbers_of;
1632 =head2 get_hostitemnumbers_of
1634 my @itemnumbers_of = get_hostitemnumbers_of($biblionumber);
1636 Given a biblionumber, return the list of corresponding itemnumbers that are linked to it via host fields
1638 Return a reference on a hash where key is a biblionumber and values are
1639 references on array of itemnumbers.
1644 sub get_hostitemnumbers_of
{
1645 my ($biblionumber) = @_;
1646 my $marcrecord = GetMarcBiblio
($biblionumber);
1647 my (@returnhostitemnumbers,$tag, $biblio_s, $item_s);
1649 my $marcflavor = C4
::Context
->preference('marcflavour');
1650 if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') {
1654 } elsif ($marcflavor eq 'UNIMARC') {
1660 foreach my $hostfield ( $marcrecord->field($tag) ) {
1661 my $hostbiblionumber = $hostfield->subfield($biblio_s);
1662 my $linkeditemnumber = $hostfield->subfield($item_s);
1664 if (my $itemnumbers = get_itemnumbers_of
($hostbiblionumber)->{$hostbiblionumber})
1666 @itemnumbers = @
$itemnumbers;
1668 foreach my $itemnumber (@itemnumbers){
1669 if ($itemnumber eq $linkeditemnumber){
1670 push (@returnhostitemnumbers,$itemnumber);
1675 return @returnhostitemnumbers;
1679 =head2 GetItemnumberFromBarcode
1681 $result = GetItemnumberFromBarcode($barcode);
1685 sub GetItemnumberFromBarcode
{
1687 my $dbh = C4
::Context
->dbh;
1690 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1691 $rq->execute($barcode);
1692 my ($result) = $rq->fetchrow;
1696 =head2 GetBarcodeFromItemnumber
1698 $result = GetBarcodeFromItemnumber($itemnumber);
1702 sub GetBarcodeFromItemnumber
{
1703 my ($itemnumber) = @_;
1704 my $dbh = C4
::Context
->dbh;
1707 $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1708 $rq->execute($itemnumber);
1709 my ($result) = $rq->fetchrow;
1713 =head2 GetHiddenItemnumbers
1715 my @itemnumbers_to_hide = GetHiddenItemnumbers(@items);
1717 Given a list of items it checks which should be hidden from the OPAC given
1718 the current configuration. Returns a list of itemnumbers corresponding to
1719 those that should be hidden.
1723 sub GetHiddenItemnumbers
{
1727 my $yaml = C4
::Context
->preference('OpacHiddenItems');
1728 return () if (! $yaml =~ /\S/ );
1729 $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
1732 $hidingrules = YAML
::Load
($yaml);
1735 warn "Unable to parse OpacHiddenItems syspref : $@";
1738 my $dbh = C4
::Context
->dbh;
1741 foreach my $item (@items) {
1743 # We check each rule
1744 foreach my $field (keys %$hidingrules) {
1746 if (exists $item->{$field}) {
1747 $val = $item->{$field};
1750 my $query = "SELECT $field from items where itemnumber = ?";
1751 $val = $dbh->selectrow_array($query, undef, $item->{'itemnumber'});
1753 $val = '' unless defined $val;
1755 # If the results matches the values in the yaml file
1756 if (any
{ $val eq $_ } @
{$hidingrules->{$field}}) {
1758 # We add the itemnumber to the list
1759 push @resultitems, $item->{'itemnumber'};
1761 # If at least one rule matched for an item, no need to test the others
1766 return @resultitems;
1769 =head1 LIMITED USE FUNCTIONS
1771 The following functions, while part of the public API,
1772 are not exported. This is generally because they are
1773 meant to be used by only one script for a specific
1774 purpose, and should not be used in any other context
1775 without careful thought.
1781 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1783 Returns MARC::Record of the item passed in parameter.
1784 This function is meant for use only in C<cataloguing/additem.pl>,
1785 where it is needed to support that script's MARC-like
1791 my ( $biblionumber, $itemnumber ) = @_;
1793 # GetMarcItem has been revised so that it does the following:
1794 # 1. Gets the item information from the items table.
1795 # 2. Converts it to a MARC field for storage in the bib record.
1797 # The previous behavior was:
1798 # 1. Get the bib record.
1799 # 2. Return the MARC tag corresponding to the item record.
1801 # The difference is that one treats the items row as authoritative,
1802 # while the other treats the MARC representation as authoritative
1803 # under certain circumstances.
1805 my $itemrecord = GetItem
($itemnumber);
1807 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1808 # Also, don't emit a subfield if the underlying field is blank.
1811 return Item2Marc
($itemrecord,$biblionumber);
1815 my ($itemrecord,$biblionumber)=@_;
1818 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ?
("items.$_" => $itemrecord->{$_}) : ()
1819 } keys %{ $itemrecord }
1821 my $itemmarc = TransformKohaToMarc
($mungeditem);
1822 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField
("items.itemnumber",GetFrameworkCode
($biblionumber)||'');
1824 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml
($mungeditem->{'items.more_subfields_xml'});
1825 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1826 foreach my $field ($itemmarc->field($itemtag)){
1827 $field->add_subfields(@
$unlinked_item_subfields);
1833 =head1 PRIVATE FUNCTIONS AND VARIABLES
1835 The following functions are not meant to be called
1836 directly, but are documented in order to explain
1837 the inner workings of C<C4::Items>.
1841 =head2 %derived_columns
1843 This hash keeps track of item columns that
1844 are strictly derived from other columns in
1845 the item record and are not meant to be set
1848 Each key in the hash should be the name of a
1849 column (as named by TransformMarcToKoha). Each
1850 value should be hashref whose keys are the
1851 columns on which the derived column depends. The
1852 hashref should also contain a 'BUILDER' key
1853 that is a reference to a sub that calculates
1858 my %derived_columns = (
1859 'items.cn_sort' => {
1860 'itemcallnumber' => 1,
1861 'items.cn_source' => 1,
1862 'BUILDER' => \
&_calc_items_cn_sort
,
1866 =head2 _set_derived_columns_for_add
1868 _set_derived_column_for_add($item);
1870 Given an item hash representing a new item to be added,
1871 calculate any derived columns. Currently the only
1872 such column is C<items.cn_sort>.
1876 sub _set_derived_columns_for_add
{
1879 foreach my $column (keys %derived_columns) {
1880 my $builder = $derived_columns{$column}->{'BUILDER'};
1881 my $source_values = {};
1882 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1883 next if $source_column eq 'BUILDER';
1884 $source_values->{$source_column} = $item->{$source_column};
1886 $builder->($item, $source_values);
1890 =head2 _set_derived_columns_for_mod
1892 _set_derived_column_for_mod($item);
1894 Given an item hash representing a new item to be modified.
1895 calculate any derived columns. Currently the only
1896 such column is C<items.cn_sort>.
1898 This routine differs from C<_set_derived_columns_for_add>
1899 in that it needs to handle partial item records. In other
1900 words, the caller of C<ModItem> may have supplied only one
1901 or two columns to be changed, so this function needs to
1902 determine whether any of the columns to be changed affect
1903 any of the derived columns. Also, if a derived column
1904 depends on more than one column, but the caller is not
1905 changing all of then, this routine retrieves the unchanged
1906 values from the database in order to ensure a correct
1911 sub _set_derived_columns_for_mod
{
1914 foreach my $column (keys %derived_columns) {
1915 my $builder = $derived_columns{$column}->{'BUILDER'};
1916 my $source_values = {};
1917 my %missing_sources = ();
1918 my $must_recalc = 0;
1919 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1920 next if $source_column eq 'BUILDER';
1921 if (exists $item->{$source_column}) {
1923 $source_values->{$source_column} = $item->{$source_column};
1925 $missing_sources{$source_column} = 1;
1929 foreach my $source_column (keys %missing_sources) {
1930 $source_values->{$source_column} = _get_single_item_column
($source_column, $item->{'itemnumber'});
1932 $builder->($item, $source_values);
1937 =head2 _do_column_fixes_for_mod
1939 _do_column_fixes_for_mod($item);
1941 Given an item hashref containing one or more
1942 columns to modify, fix up certain values.
1943 Specifically, set to 0 any passed value
1944 of C<notforloan>, C<damaged>, C<itemlost>, or
1945 C<withdrawn> that is either undefined or
1946 contains the empty string.
1950 sub _do_column_fixes_for_mod
{
1953 if (exists $item->{'notforloan'} and
1954 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1955 $item->{'notforloan'} = 0;
1957 if (exists $item->{'damaged'} and
1958 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1959 $item->{'damaged'} = 0;
1961 if (exists $item->{'itemlost'} and
1962 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1963 $item->{'itemlost'} = 0;
1965 if (exists $item->{'withdrawn'} and
1966 (not defined $item->{'withdrawn'} or $item->{'withdrawn'} eq '')) {
1967 $item->{'withdrawn'} = 0;
1969 if (exists $item->{location
}
1970 and $item->{location
} ne 'CART'
1971 and $item->{location
} ne 'PROC'
1972 and not $item->{permanent_location
}
1974 $item->{'permanent_location'} = $item->{'location'};
1976 if (exists $item->{'timestamp'}) {
1977 delete $item->{'timestamp'};
1981 =head2 _get_single_item_column
1983 _get_single_item_column($column, $itemnumber);
1985 Retrieves the value of a single column from an C<items>
1986 row specified by C<$itemnumber>.
1990 sub _get_single_item_column
{
1992 my $itemnumber = shift;
1994 my $dbh = C4
::Context
->dbh;
1995 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1996 $sth->execute($itemnumber);
1997 my ($value) = $sth->fetchrow();
2001 =head2 _calc_items_cn_sort
2003 _calc_items_cn_sort($item, $source_values);
2005 Helper routine to calculate C<items.cn_sort>.
2009 sub _calc_items_cn_sort
{
2011 my $source_values = shift;
2013 $item->{'items.cn_sort'} = GetClassSort
($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
2016 =head2 _set_defaults_for_add
2018 _set_defaults_for_add($item_hash);
2020 Given an item hash representing an item to be added, set
2021 correct default values for columns whose default value
2022 is not handled by the DBMS. This includes the following
2029 C<items.dateaccessioned>
2051 sub _set_defaults_for_add
{
2053 $item->{dateaccessioned
} ||= output_pref
({ dt
=> dt_from_string
, dateformat
=> 'iso', dateonly
=> 1 });
2054 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost withdrawn));
2057 =head2 _koha_new_item
2059 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
2061 Perform the actual insert into the C<items> table.
2065 sub _koha_new_item
{
2066 my ( $item, $barcode ) = @_;
2067 my $dbh=C4
::Context
->dbh;
2069 $item->{permanent_location
} //= $item->{location
};
2071 "INSERT INTO items SET
2073 biblioitemnumber = ?,
2075 dateaccessioned = ?,
2079 replacementprice = ?,
2080 replacementpricedate = ?,
2081 datelastborrowed = ?,
2089 coded_location_qualifier = ?,
2092 itemnotes_nonpublic = ?,
2096 permanent_location = ?,
2108 more_subfields_xml = ?,
2113 my $sth = $dbh->prepare($query);
2114 my $today = output_pref
({ dt
=> dt_from_string
, dateformat
=> 'iso', dateonly
=> 1 });
2116 $item->{'biblionumber'},
2117 $item->{'biblioitemnumber'},
2119 $item->{'dateaccessioned'},
2120 $item->{'booksellerid'},
2121 $item->{'homebranch'},
2123 $item->{'replacementprice'},
2124 $item->{'replacementpricedate'} || $today,
2125 $item->{datelastborrowed
},
2126 $item->{datelastseen
} || $today,
2128 $item->{'notforloan'},
2130 $item->{'itemlost'},
2131 $item->{'withdrawn'},
2132 $item->{'itemcallnumber'},
2133 $item->{'coded_location_qualifier'},
2134 $item->{'restricted'},
2135 $item->{'itemnotes'},
2136 $item->{'itemnotes_nonpublic'},
2137 $item->{'holdingbranch'},
2139 $item->{'location'},
2140 $item->{'permanent_location'},
2143 $item->{'renewals'},
2144 $item->{'reserves'},
2145 $item->{'items.cn_source'},
2146 $item->{'items.cn_sort'},
2149 $item->{'materials'},
2151 $item->{'enumchron'},
2152 $item->{'more_subfields_xml'},
2153 $item->{'copynumber'},
2154 $item->{'stocknumber'},
2155 $item->{'new_status'},
2159 if ( defined $sth->errstr ) {
2160 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2163 $itemnumber = $dbh->{'mysql_insertid'};
2166 return ( $itemnumber, $error );
2169 =head2 MoveItemFromBiblio
2171 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2173 Moves an item from a biblio to another
2175 Returns undef if the move failed or the biblionumber of the destination record otherwise
2179 sub MoveItemFromBiblio
{
2180 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2181 my $dbh = C4
::Context
->dbh;
2182 my ( $tobiblioitem ) = $dbh->selectrow_array(q
|
2183 SELECT biblioitemnumber
2185 WHERE biblionumber
= ?
2186 |, undef, $tobiblio );
2187 my $return = $dbh->do(q
|
2189 SET biblioitemnumber
= ?
,
2191 WHERE itemnumber
= ?
2192 AND biblionumber
= ?
2193 |, undef, $tobiblioitem, $tobiblio, $itemnumber, $frombiblio );
2195 ModZebra
( $tobiblio, "specialUpdate", "biblioserver" );
2196 ModZebra
( $frombiblio, "specialUpdate", "biblioserver" );
2197 # Checking if the item we want to move is in an order
2198 require C4
::Acquisition
;
2199 my $order = C4
::Acquisition
::GetOrderFromItemnumber
($itemnumber);
2201 # Replacing the biblionumber within the order if necessary
2202 $order->{'biblionumber'} = $tobiblio;
2203 C4
::Acquisition
::ModOrder
($order);
2206 # Update reserves, hold_fill_targets, tmp_holdsqueue and linktracker tables
2207 for my $table_name ( qw( reserves hold_fill_targets tmp_holdsqueue linktracker ) ) {
2210 SET biblionumber
= ?
2211 WHERE itemnumber
= ?
2212 |, undef, $tobiblio, $itemnumber );
2221 DelItemCheck($dbh, $biblionumber, $itemnumber);
2223 Exported function (core API) for deleting an item record in Koha if there no current issue.
2228 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2230 $dbh ||= C4
::Context
->dbh;
2234 my $countanalytics=GetAnalyticsCount
($itemnumber);
2237 # check that there is no issue on this item before deletion.
2238 my $sth = $dbh->prepare(q{
2239 SELECT COUNT(*) FROM issues
2240 WHERE itemnumber = ?
2242 $sth->execute($itemnumber);
2243 my ($onloan) = $sth->fetchrow;
2245 my $item = GetItem
($itemnumber);
2248 $error = "book_on_loan"
2250 elsif ( defined C4
::Context
->userenv
2251 and !C4
::Context
->IsSuperLibrarian()
2252 and C4
::Context
->preference("IndependentBranches")
2253 and ( C4
::Context
->userenv->{branch
} ne $item->{'homebranch'} ) )
2255 $error = "not_same_branch";
2258 # check it doesn't have a waiting reserve
2259 $sth = $dbh->prepare(q{
2260 SELECT COUNT(*) FROM reserves
2261 WHERE (found = 'W' OR found = 'T')
2264 $sth->execute($itemnumber);
2265 my ($reserve) = $sth->fetchrow;
2267 $error = "book_reserved";
2268 } elsif ($countanalytics > 0){
2269 $error = "linked_analytics";
2273 biblionumber
=> $biblionumber,
2274 itemnumber
=> $itemnumber
2283 =head2 _koha_modify_item
2285 my ($itemnumber,$error) =_koha_modify_item( $item );
2287 Perform the actual update of the C<items> row. Note that this
2288 routine accepts a hashref specifying the columns to update.
2292 sub _koha_modify_item
{
2294 my $dbh=C4
::Context
->dbh;
2297 my $query = "UPDATE items SET ";
2299 for my $key ( keys %$item ) {
2300 next if ( $key eq 'itemnumber' );
2302 push @bind, $item->{$key};
2305 $query .= " WHERE itemnumber=?";
2306 push @bind, $item->{'itemnumber'};
2307 my $sth = $dbh->prepare($query);
2308 $sth->execute(@bind);
2310 $error.="ERROR in _koha_modify_item $query: ".$sth->errstr;
2313 return ($item->{'itemnumber'},$error);
2316 =head2 _koha_delete_item
2318 _koha_delete_item( $itemnum );
2320 Internal function to delete an item record from the koha tables
2324 sub _koha_delete_item
{
2325 my ( $itemnum ) = @_;
2327 my $dbh = C4
::Context
->dbh;
2328 # save the deleted item to deleteditems table
2329 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2330 $sth->execute($itemnum);
2331 my $data = $sth->fetchrow_hashref();
2333 # There is no item to delete
2334 return 0 unless $data;
2336 my $query = "INSERT INTO deleteditems SET ";
2338 foreach my $key ( keys %$data ) {
2339 next if ( $key eq 'timestamp' ); # timestamp will be set by db
2340 $query .= "$key = ?,";
2341 push( @bind, $data->{$key} );
2344 $sth = $dbh->prepare($query);
2345 $sth->execute(@bind);
2347 # delete from items table
2348 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2349 my $deleted = $sth->execute($itemnum);
2350 return ( $deleted == 1 ) ?
1 : 0;
2353 =head2 _marc_from_item_hash
2355 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2357 Given an item hash representing a complete item record,
2358 create a C<MARC::Record> object containing an embedded
2359 tag representing that item.
2361 The third, optional parameter C<$unlinked_item_subfields> is
2362 an arrayref of subfields (not mapped to C<items> fields per the
2363 framework) to be added to the MARC representation
2368 sub _marc_from_item_hash
{
2370 my $frameworkcode = shift;
2371 my $unlinked_item_subfields;
2373 $unlinked_item_subfields = shift;
2376 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2377 # Also, don't emit a subfield if the underlying field is blank.
2378 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2379 (/^items\./ ?
($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2380 : () } keys %{ $item } };
2382 my $item_marc = MARC
::Record
->new();
2383 foreach my $item_field ( keys %{$mungeditem} ) {
2384 my ( $tag, $subfield ) = GetMarcFromKohaField
( $item_field, $frameworkcode );
2385 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2386 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2387 foreach my $value (@values){
2388 if ( my $field = $item_marc->field($tag) ) {
2389 $field->add_subfields( $subfield => $value );
2391 my $add_subfields = [];
2392 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2393 $add_subfields = $unlinked_item_subfields;
2395 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @
$add_subfields );
2403 =head2 _repack_item_errors
2405 Add an error message hash generated by C<CheckItemPreSave>
2406 to a list of errors.
2410 sub _repack_item_errors
{
2411 my $item_sequence_num = shift;
2412 my $item_ref = shift;
2413 my $error_ref = shift;
2415 my @repacked_errors = ();
2417 foreach my $error_code (sort keys %{ $error_ref }) {
2418 my $repacked_error = {};
2419 $repacked_error->{'item_sequence'} = $item_sequence_num;
2420 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ?
$item_ref->{'barcode'} : '';
2421 $repacked_error->{'error_code'} = $error_code;
2422 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2423 push @repacked_errors, $repacked_error;
2426 return @repacked_errors;
2429 =head2 _get_unlinked_item_subfields
2431 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2435 sub _get_unlinked_item_subfields
{
2436 my $original_item_marc = shift;
2437 my $frameworkcode = shift;
2439 my $marcstructure = GetMarcStructure
(1, $frameworkcode);
2441 # assume that this record has only one field, and that that
2442 # field contains only the item information
2444 my @fields = $original_item_marc->fields();
2445 if ($#fields > -1) {
2446 my $field = $fields[0];
2447 my $tag = $field->tag();
2448 foreach my $subfield ($field->subfields()) {
2449 if (defined $subfield->[1] and
2450 $subfield->[1] ne '' and
2451 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2452 push @
$subfields, $subfield->[0] => $subfield->[1];
2459 =head2 _get_unlinked_subfields_xml
2461 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2465 sub _get_unlinked_subfields_xml
{
2466 my $unlinked_item_subfields = shift;
2469 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2470 my $marc = MARC
::Record
->new();
2471 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2472 # used in the framework
2473 $marc->append_fields(MARC
::Field
->new('999', ' ', ' ', @
$unlinked_item_subfields));
2474 $marc->encoding("UTF-8");
2475 $xml = $marc->as_xml("USMARC");
2481 =head2 _parse_unlinked_item_subfields_from_xml
2483 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2487 sub _parse_unlinked_item_subfields_from_xml
{
2489 require C4
::Charset
;
2490 return unless defined $xml and $xml ne "";
2491 my $marc = MARC
::Record
->new_from_xml(C4
::Charset
::StripNonXmlChars
($xml),'UTF-8');
2492 my $unlinked_subfields = [];
2493 my @fields = $marc->fields();
2494 if ($#fields > -1) {
2495 foreach my $subfield ($fields[0]->subfields()) {
2496 push @
$unlinked_subfields, $subfield->[0] => $subfield->[1];
2499 return $unlinked_subfields;
2502 =head2 GetAnalyticsCount
2504 $count= &GetAnalyticsCount($itemnumber)
2506 counts Usage of itemnumber in Analytical bibliorecords.
2510 sub GetAnalyticsCount
{
2511 my ($itemnumber) = @_;
2513 ### ZOOM search here
2515 $query= "hi=".$itemnumber;
2516 my $searcher = Koha
::SearchEngine
::Search
->new({index => $Koha::SearchEngine
::BIBLIOS_INDEX
});
2517 my ($err,$res,$result) = $searcher->simple_search_compat($query,0,10);
2523 $holds = &GetItemHolds($biblionumber, $itemnumber);
2525 This function return the count of holds with $biblionumber and $itemnumber
2530 my ($biblionumber, $itemnumber) = @_;
2532 my $dbh = C4
::Context
->dbh;
2533 my $query = "SELECT count(*)
2535 WHERE biblionumber=? AND itemnumber=?";
2536 my $sth = $dbh->prepare($query);
2537 $sth->execute($biblionumber, $itemnumber);
2538 $holds = $sth->fetchrow;
2542 =head2 SearchItemsByField
2544 my $items = SearchItemsByField($field, $value);
2546 SearchItemsByField will search for items on a specific given field.
2547 For instance you can search all items with a specific stocknumber like this:
2549 my $items = SearchItemsByField('stocknumber', $stocknumber);
2553 sub SearchItemsByField
{
2554 my ($field, $value) = @_;
2561 my ($results) = SearchItems
($filters);
2565 sub _SearchItems_build_where_fragment
{
2568 my $dbh = C4
::Context
->dbh;
2571 if (exists($filter->{conjunction
})) {
2572 my (@where_strs, @where_args);
2573 foreach my $f (@
{ $filter->{filters
} }) {
2574 my $fragment = _SearchItems_build_where_fragment
($f);
2576 push @where_strs, $fragment->{str
};
2577 push @where_args, @
{ $fragment->{args
} };
2582 $where_str = '(' . join (' ' . $filter->{conjunction
} . ' ', @where_strs) . ')';
2585 args
=> \
@where_args,
2589 my @columns = Koha
::Database
->new()->schema()->resultset('Item')->result_source->columns;
2590 push @columns, Koha
::Database
->new()->schema()->resultset('Biblio')->result_source->columns;
2591 push @columns, Koha
::Database
->new()->schema()->resultset('Biblioitem')->result_source->columns;
2592 my @operators = qw(= != > < >= <= like);
2593 my $field = $filter->{field
};
2594 if ( (0 < grep /^$field$/, @columns) or (substr($field, 0, 5) eq 'marc:') ) {
2595 my $op = $filter->{operator
};
2596 my $query = $filter->{query
};
2598 if (!$op or (0 == grep /^$op$/, @operators)) {
2599 $op = '='; # default operator
2603 if ($field =~ /^marc:(\d{3})(?:\$(\w))?$/) {
2605 my $marcsubfield = $2;
2606 my ($kohafield) = $dbh->selectrow_array(q
|
2607 SELECT kohafield FROM marc_subfield_structure
2608 WHERE tagfield
=? AND tagsubfield
=? AND frameworkcode
=''
2609 |, undef, $marcfield, $marcsubfield);
2612 $column = $kohafield;
2614 # MARC field is not linked to a DB field so we need to use
2615 # ExtractValue on biblioitems.marcxml or
2616 # items.more_subfields_xml, depending on the MARC field.
2619 my ($itemfield) = GetMarcFromKohaField
('items.itemnumber');
2620 if ($marcfield eq $itemfield) {
2621 $sqlfield = 'more_subfields_xml';
2622 $xpath = '//record/datafield/subfield[@code="' . $marcsubfield . '"]';
2624 $sqlfield = 'marcxml';
2625 if ($marcfield < 10) {
2626 $xpath = "//record/controlfield[\@tag=\"$marcfield\"]";
2628 $xpath = "//record/datafield[\@tag=\"$marcfield\"]/subfield[\@code=\"$marcsubfield\"]";
2631 $column = "ExtractValue($sqlfield, '$xpath')";
2637 if (ref $query eq 'ARRAY') {
2640 } elsif ($op eq '!=') {
2644 str
=> "$column $op (" . join (',', ('?') x @
$query) . ")",
2649 str
=> "$column $op ?",
2656 return $where_fragment;
2661 my ($items, $total) = SearchItems($filter, $params);
2663 Perform a search among items
2665 $filter is a reference to a hash which can be a filter, or a combination of filters.
2667 A filter has the following keys:
2671 =item * field: the name of a SQL column in table items
2673 =item * query: the value to search in this column
2675 =item * operator: comparison operator. Can be one of = != > < >= <= like
2679 A combination of filters hash the following keys:
2683 =item * conjunction: 'AND' or 'OR'
2685 =item * filters: array ref of filters
2689 $params is a reference to a hash that can contain the following parameters:
2693 =item * rows: Number of items to return. 0 returns everything (default: 0)
2695 =item * page: Page to return (return items from (page-1)*rows to (page*rows)-1)
2698 =item * sortby: A SQL column name in items table to sort on
2700 =item * sortorder: 'ASC' or 'DESC'
2707 my ($filter, $params) = @_;
2711 return unless ref $filter eq 'HASH';
2712 return unless ref $params eq 'HASH';
2714 # Default parameters
2715 $params->{rows
} ||= 0;
2716 $params->{page
} ||= 1;
2717 $params->{sortby
} ||= 'itemnumber';
2718 $params->{sortorder
} ||= 'ASC';
2720 my ($where_str, @where_args);
2721 my $where_fragment = _SearchItems_build_where_fragment
($filter);
2722 if ($where_fragment) {
2723 $where_str = $where_fragment->{str
};
2724 @where_args = @
{ $where_fragment->{args
} };
2727 my $dbh = C4
::Context
->dbh;
2729 SELECT SQL_CALC_FOUND_ROWS items.*
2731 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
2732 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
2734 if (defined $where_str and $where_str ne '') {
2735 $query .= qq{ WHERE
$where_str };
2738 my @columns = Koha
::Database
->new()->schema()->resultset('Item')->result_source->columns;
2739 push @columns, Koha
::Database
->new()->schema()->resultset('Biblio')->result_source->columns;
2740 push @columns, Koha
::Database
->new()->schema()->resultset('Biblioitem')->result_source->columns;
2741 my $sortby = (0 < grep {$params->{sortby
} eq $_} @columns)
2742 ?
$params->{sortby
} : 'itemnumber';
2743 my $sortorder = (uc($params->{sortorder
}) eq 'ASC') ?
'ASC' : 'DESC';
2744 $query .= qq{ ORDER BY
$sortby $sortorder };
2746 my $rows = $params->{rows
};
2749 my $offset = $rows * ($params->{page
}-1);
2750 $query .= qq { LIMIT ?
, ?
};
2751 push @limit_args, $offset, $rows;
2754 my $sth = $dbh->prepare($query);
2755 my $rv = $sth->execute(@where_args, @limit_args);
2757 return unless ($rv);
2758 my ($total_rows) = $dbh->selectrow_array(q{ SELECT FOUND_ROWS() });
2760 return ($sth->fetchall_arrayref({}), $total_rows);
2764 =head1 OTHER FUNCTIONS
2768 ($indicators, $value) = _find_value($tag, $subfield, $record,$encoding);
2770 Find the given $subfield in the given $tag in the given
2771 MARC::Record $record. If the subfield is found, returns
2772 the (indicators, value) pair; otherwise, (undef, undef) is
2776 Such a function is used in addbiblio AND additem and serial-edit and maybe could be used in Authorities.
2777 I suggest we export it from this module.
2782 my ( $tagfield, $insubfield, $record, $encoding ) = @_;
2785 if ( $tagfield < 10 ) {
2786 if ( $record->field($tagfield) ) {
2787 push @result, $record->field($tagfield)->data();
2792 foreach my $field ( $record->field($tagfield) ) {
2793 my @subfields = $field->subfields();
2794 foreach my $subfield (@subfields) {
2795 if ( @
$subfield[0] eq $insubfield ) {
2796 push @result, @
$subfield[1];
2797 $indicator = $field->indicator(1) . $field->indicator(2);
2802 return ( $indicator, @result );
2806 =head2 PrepareItemrecordDisplay
2808 PrepareItemrecordDisplay($itemrecord,$bibnum,$itemumber,$frameworkcode);
2810 Returns a hash with all the fields for Display a given item data in a template
2812 The $frameworkcode returns the item for the given frameworkcode, ONLY if bibnum is not provided
2816 sub PrepareItemrecordDisplay
{
2818 my ( $bibnum, $itemnum, $defaultvalues, $frameworkcode ) = @_;
2820 my $dbh = C4
::Context
->dbh;
2821 $frameworkcode = &GetFrameworkCode
($bibnum) if $bibnum;
2822 my ( $itemtagfield, $itemtagsubfield ) = &GetMarcFromKohaField
( "items.itemnumber", $frameworkcode );
2823 my $tagslib = &GetMarcStructure
( 1, $frameworkcode );
2825 # return nothing if we don't have found an existing framework.
2826 return q{} unless $tagslib;
2829 $itemrecord = C4
::Items
::GetMarcItem
( $bibnum, $itemnum );
2833 my $branch_limit = C4
::Context
->userenv ? C4
::Context
->userenv->{"branch"} : "";
2835 SELECT authorised_value
,lib FROM authorised_values
2838 LEFT JOIN authorised_values_branches ON
( id
= av_id
)
2843 $query .= qq{ AND
( branchcode
= ? OR branchcode IS NULL
)} if $branch_limit;
2844 $query .= qq{ ORDER BY lib
};
2845 my $authorised_values_sth = $dbh->prepare( $query );
2846 foreach my $tag ( sort keys %{$tagslib} ) {
2847 my $previous_tag = '';
2850 # loop through each subfield
2852 foreach my $subfield ( sort keys %{ $tagslib->{$tag} } ) {
2853 next if IsMarcStructureInternal
($tagslib->{$tag}{$subfield});
2854 next if ( $tagslib->{$tag}->{$subfield}->{'tab'} ne "10" );
2856 $subfield_data{tag
} = $tag;
2857 $subfield_data{subfield
} = $subfield;
2858 $subfield_data{countsubfield
} = $cntsubf++;
2859 $subfield_data{kohafield
} = $tagslib->{$tag}->{$subfield}->{'kohafield'};
2860 $subfield_data{id
} = "tag_".$tag."_subfield_".$subfield."_".int(rand(1000000));
2862 # $subfield_data{marc_lib}=$tagslib->{$tag}->{$subfield}->{lib};
2863 $subfield_data{marc_lib
} = $tagslib->{$tag}->{$subfield}->{lib
};
2864 $subfield_data{mandatory
} = $tagslib->{$tag}->{$subfield}->{mandatory
};
2865 $subfield_data{repeatable
} = $tagslib->{$tag}->{$subfield}->{repeatable
};
2866 $subfield_data{hidden
} = "display:none"
2867 if ( ( $tagslib->{$tag}->{$subfield}->{hidden
} > 4 )
2868 || ( $tagslib->{$tag}->{$subfield}->{hidden
} < -4 ) );
2869 my ( $x, $defaultvalue );
2871 ( $x, $defaultvalue ) = _find_value
( $tag, $subfield, $itemrecord );
2873 $defaultvalue = $tagslib->{$tag}->{$subfield}->{defaultvalue
} unless $defaultvalue;
2874 if ( !defined $defaultvalue ) {
2875 $defaultvalue = q
||;
2877 $defaultvalue =~ s/"/"/g;
2880 # search for itemcallnumber if applicable
2881 if ( $tagslib->{$tag}->{$subfield}->{kohafield
} eq 'items.itemcallnumber'
2882 && C4
::Context
->preference('itemcallnumber') ) {
2883 my $CNtag = substr( C4
::Context
->preference('itemcallnumber'), 0, 3 );
2884 my $CNsubfield = substr( C4
::Context
->preference('itemcallnumber'), 3, 1 );
2885 if ( $itemrecord and my $field = $itemrecord->field($CNtag) ) {
2886 $defaultvalue = $field->subfield($CNsubfield);
2889 if ( $tagslib->{$tag}->{$subfield}->{kohafield
} eq 'items.itemcallnumber'
2891 && $defaultvalues->{'callnumber'} ) {
2892 if( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ){
2893 # if the item record exists, only use default value if the item has no callnumber
2894 $defaultvalue = $defaultvalues->{callnumber
};
2895 } elsif ( !$itemrecord and $defaultvalues ) {
2896 # if the item record *doesn't* exists, always use the default value
2897 $defaultvalue = $defaultvalues->{callnumber
};
2900 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield
} eq 'items.holdingbranch' || $tagslib->{$tag}->{$subfield}->{kohafield
} eq 'items.homebranch' )
2902 && $defaultvalues->{'branchcode'} ) {
2903 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2904 $defaultvalue = $defaultvalues->{branchcode
};
2907 if ( ( $tagslib->{$tag}->{$subfield}->{kohafield
} eq 'items.location' )
2909 && $defaultvalues->{'location'} ) {
2911 if ( $itemrecord and $defaultvalues and not $itemrecord->field($subfield) ) {
2912 # if the item record exists, only use default value if the item has no locationr
2913 $defaultvalue = $defaultvalues->{location
};
2914 } elsif ( !$itemrecord and $defaultvalues ) {
2915 # if the item record *doesn't* exists, always use the default value
2916 $defaultvalue = $defaultvalues->{location
};
2919 if ( $tagslib->{$tag}->{$subfield}->{authorised_value
} ) {
2920 my @authorised_values;
2923 # builds list, depending on authorised value...
2925 if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
2926 if ( ( C4
::Context
->preference("IndependentBranches") )
2927 && !C4
::Context
->IsSuperLibrarian() ) {
2928 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
2929 $sth->execute( C4
::Context
->userenv->{branch
} );
2930 push @authorised_values, ""
2931 unless ( $tagslib->{$tag}->{$subfield}->{mandatory
} );
2932 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2933 push @authorised_values, $branchcode;
2934 $authorised_lib{$branchcode} = $branchname;
2937 my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches ORDER BY branchname" );
2939 push @authorised_values, ""
2940 unless ( $tagslib->{$tag}->{$subfield}->{mandatory
} );
2941 while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
2942 push @authorised_values, $branchcode;
2943 $authorised_lib{$branchcode} = $branchname;
2947 $defaultvalue = C4
::Context
->userenv ? C4
::Context
->userenv->{branch
} : undef;
2948 if ( $defaultvalues and $defaultvalues->{branchcode
} ) {
2949 $defaultvalue = $defaultvalues->{branchcode
};
2953 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value
} eq "itemtypes" ) {
2954 my $itemtypes = GetItemTypes
( style
=> 'array' );
2955 push @authorised_values, ""
2956 unless ( $tagslib->{$tag}->{$subfield}->{mandatory
} );
2957 for my $itemtype ( @
$itemtypes ) {
2958 push @authorised_values, $itemtype->{itemtype
};
2959 $authorised_lib{$itemtype->{itemtype
}} = $itemtype->{translated_description
};
2962 } elsif ( $tagslib->{$tag}->{$subfield}->{authorised_value
} eq "cn_source" ) {
2963 push @authorised_values, "" unless ( $tagslib->{$tag}->{$subfield}->{mandatory
} );
2965 my $class_sources = GetClassSources
();
2966 my $default_source = C4
::Context
->preference("DefaultClassificationSource");
2968 foreach my $class_source (sort keys %$class_sources) {
2969 next unless $class_sources->{$class_source}->{'used'} or
2970 ($class_source eq $default_source);
2971 push @authorised_values, $class_source;
2972 $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'};
2975 $defaultvalue = $default_source;
2977 #---- "true" authorised value
2979 $authorised_values_sth->execute(
2980 $tagslib->{$tag}->{$subfield}->{authorised_value
},
2981 $branch_limit ?
$branch_limit : ()
2983 push @authorised_values, ""
2984 unless ( $tagslib->{$tag}->{$subfield}->{mandatory
} );
2985 while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) {
2986 push @authorised_values, $value;
2987 $authorised_lib{$value} = $lib;
2990 $subfield_data{marc_value
} = {
2992 values => \
@authorised_values,
2993 default => "$defaultvalue",
2994 labels
=> \
%authorised_lib,
2996 } elsif ( $tagslib->{$tag}->{$subfield}->{value_builder
} ) {
2998 require Koha
::FrameworkPlugin
;
2999 my $plugin = Koha
::FrameworkPlugin
->new({
3000 name
=> $tagslib->{$tag}->{$subfield}->{value_builder
},
3003 my $pars = { dbh
=> $dbh, record
=> undef, tagslib
=>$tagslib, id
=> $subfield_data{id
}, tabloop
=> undef };
3004 $plugin->build( $pars );
3005 if( !$plugin->errstr ) {
3006 #TODO Move html to template; see report 12176/13397
3007 my $tab= $plugin->noclick?
'-1': '';
3008 my $class= $plugin->noclick?
' disabled': '';
3009 my $title= $plugin->noclick?
'No popup': 'Tag editor';
3010 $subfield_data{marc_value
} = qq[<input type
="text" id
="$subfield_data{id}" name
="field_value" class="input_marceditor" size
="50" maxlength
="255" /><a href="#" id="buttonDot_$subfield_data{id}" tabindex="$tab" class="buttonDot $class" title="$title">...</a>\n].$plugin->javascript;
3012 warn $plugin->errstr;
3013 $subfield_data{marc_value
} = qq(<input type
="text" id
="$subfield_data{id}" name
="field_value" class="input_marceditor" size
="50" maxlength
="255" />); # supply default input form
3016 elsif ( $tag eq '' ) { # it's an hidden field
3017 $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" />);
3019 elsif ( $tagslib->{$tag}->{$subfield}->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ?
3020 $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" />);
3022 elsif ( length($defaultvalue) > 100
3023 or (C4
::Context
->preference("marcflavour") eq "UNIMARC" and
3024 300 <= $tag && $tag < 400 && $subfield eq 'a' )
3025 or (C4
::Context
->preference("marcflavour") eq "MARC21" and
3026 500 <= $tag && $tag < 600 )
3028 # oversize field (textarea)
3029 $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");
3031 $subfield_data{marc_value} = "<input type
=\"text
\" name
=\"field_value
\" value
=\"$defaultvalue\" size
=\"50\" maxlength
=\"255\" />";
3033 push( @loop_data, \%subfield_data );
3038 if ( $itemrecord && $itemrecord->field($itemtagfield) ) {
3039 $itemnumber = $itemrecord->subfield( $itemtagfield, $itemtagsubfield );
3042 'itemtagfield' => $itemtagfield,
3043 'itemtagsubfield' => $itemtagsubfield,
3044 'itemnumber' => $itemnumber,
3045 'iteminformation' => \@loop_data
3051 my @columns = C4::Items::columns();
3053 Returns an array of items' table columns on success,
3054 and an empty array on failure.
3059 my $rs = Koha::Database->new->schema->resultset('Item');
3060 return $rs->result_source->columns;
3063 =head2 biblioitems_columns
3065 my @columns = C4::Items::biblioitems_columns();
3067 Returns an array of biblioitems' table columns on success,
3068 and an empty array on failure.
3072 sub biblioitems_columns {
3073 my $rs = Koha::Database->new->schema->resultset('Biblioitem');
3074 return $rs->result_source->columns;
3077 sub ToggleNewStatus {
3078 my ( $params ) = @_;
3079 my @rules = @{ $params->{rules} };
3080 my $report_only = $params->{report_only};
3082 my $dbh = C4::Context->dbh;
3084 my @item_columns = map { "items
.$_" } C4::Items::columns;
3085 my @biblioitem_columns = map { "biblioitems
.$_" } C4::Items::biblioitems_columns;
3087 for my $rule ( @rules ) {
3088 my $age = $rule->{age};
3089 my $conditions = $rule->{conditions};
3090 my $substitutions = $rule->{substitutions};
3094 SELECT items.biblionumber, items.itemnumber
3096 LEFT JOIN biblioitems ON biblioitems.biblionumber = items.biblionumber
3099 for my $condition ( @$conditions ) {
3101 grep {/^$condition->{field}$/} @item_columns
3102 or grep {/^$condition->{field}$/} @biblioitem_columns
3104 if ( $condition->{value} =~ /\|/ ) {
3105 my @values = split /\|/, $condition->{value};
3106 $query .= qq| AND $condition->{field} IN (|
3107 . join( ',', ('?') x scalar @values )
3109 push @params, @values;
3111 $query .= qq| AND $condition->{field} = ?|;
3112 push @params, $condition->{value};
3116 if ( defined $age ) {
3117 $query .= q| AND TO_DAYS(NOW()) - TO_DAYS(dateaccessioned) >= ? |;
3120 my $sth = $dbh->prepare($query);
3121 $sth->execute( @params );
3122 while ( my $values = $sth->fetchrow_hashref ) {
3123 my $biblionumber = $values->{biblionumber};
3124 my $itemnumber = $values->{itemnumber};
3125 my $item = C4::Items::GetItem( $itemnumber );
3126 for my $substitution ( @$substitutions ) {
3127 next unless $substitution->{field};
3128 C4::Items::ModItem( {$substitution->{field} => $substitution->{value}}, $biblionumber, $itemnumber )
3129 unless $report_only;
3130 push @{ $report->{$itemnumber} }, $substitution;