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
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.
22 #use warnings; FIXME - Bug 2505
28 use C4::Dates qw/format_date format_date_in_iso/;
36 use List::MoreUtils qw/any/;
38 use vars qw($VERSION @ISA @EXPORT);
44 @ISA = qw( Exporter );
67 GetItemsByBiblioitemnumber
71 GetItemnumberFromBarcode
72 GetBarcodeFromItemnumber
84 C4::Items - item management functions
88 This module contains an API for manipulating item
89 records in Koha, and is used by cataloguing, circulation,
90 acquisitions, and serials management.
92 A Koha item record is stored in two places: the
93 items table and embedded in a MARC tag in the XML
94 version of the associated bib record in C<biblioitems.marcxml>.
95 This is done to allow the item information to be readily
96 indexed (e.g., by Zebra), but means that each item
97 modification transaction must keep the items table
98 and the MARC XML in sync at all times.
100 Consequently, all code that creates, modifies, or deletes
101 item records B<must> use an appropriate function from
102 C<C4::Items>. If no existing function is suitable, it is
103 better to add one to C<C4::Items> than to use add
104 one-off SQL statements to add or modify items.
106 The items table will be considered authoritative. In other
107 words, if there is ever a discrepancy between the items
108 table and the MARC XML, the items table should be considered
111 =head1 HISTORICAL NOTE
113 Most of the functions in C<C4::Items> were originally in
114 the C<C4::Biblio> module.
116 =head1 CORE EXPORTED FUNCTIONS
118 The following functions are meant for use by users
125 $item = GetItem($itemnumber,$barcode,$serial);
127 Return item information, for a given itemnumber or barcode.
128 The return value is a hashref mapping item column
129 names to values. If C<$serial> is true, include serial publication data.
134 my ($itemnumber,$barcode, $serial) = @_;
135 my $dbh = C4::Context->dbh;
138 my $sth = $dbh->prepare("
140 WHERE itemnumber = ?");
141 $sth->execute($itemnumber);
142 $data = $sth->fetchrow_hashref;
144 my $sth = $dbh->prepare("
148 $sth->execute($barcode);
149 $data = $sth->fetchrow_hashref;
152 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
153 $ssth->execute($data->{'itemnumber'}) ;
154 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
156 #if we don't have an items.itype, use biblioitems.itemtype.
157 if( ! $data->{'itype'} ) {
158 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
159 $sth->execute($data->{'biblionumber'});
160 ($data->{'itype'}) = $sth->fetchrow_array;
167 CartToShelf($itemnumber);
169 Set the current shelving location of the item record
170 to its stored permanent shelving location. This is
171 primarily used to indicate when an item whose current
172 location is a special processing ('PROC') or shelving cart
173 ('CART') location is back in the stacks.
178 my ( $itemnumber ) = @_;
180 unless ( $itemnumber ) {
181 croak "FAILED CartToShelf() - no itemnumber supplied";
184 my $item = GetItem($itemnumber);
185 $item->{location} = $item->{permanent_location};
186 ModItem($item, undef, $itemnumber);
189 =head2 AddItemFromMarc
191 my ($biblionumber, $biblioitemnumber, $itemnumber)
192 = AddItemFromMarc($source_item_marc, $biblionumber);
194 Given a MARC::Record object containing an embedded item
195 record and a biblionumber, create a new item record.
199 sub AddItemFromMarc {
200 my ( $source_item_marc, $biblionumber ) = @_;
201 my $dbh = C4::Context->dbh;
203 # parse item hash from MARC
204 my $frameworkcode = GetFrameworkCode( $biblionumber );
205 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
207 my $localitemmarc=MARC::Record->new;
208 $localitemmarc->append_fields($source_item_marc->field($itemtag));
209 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
210 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
211 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
216 my ($biblionumber, $biblioitemnumber, $itemnumber)
217 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
219 Given a hash containing item column names as keys,
220 create a new Koha item record.
222 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
223 do not need to be supplied for general use; they exist
224 simply to allow them to be picked up from AddItemFromMarc.
226 The final optional parameter, C<$unlinked_item_subfields>, contains
227 an arrayref containing subfields present in the original MARC
228 representation of the item (e.g., from the item editor) that are
229 not mapped to C<items> columns directly but should instead
230 be stored in C<items.more_subfields_xml> and included in
231 the biblio items tag for display and indexing.
237 my $biblionumber = shift;
239 my $dbh = @_ ? shift : C4::Context->dbh;
240 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
241 my $unlinked_item_subfields;
243 $unlinked_item_subfields = shift
246 # needs old biblionumber and biblioitemnumber
247 $item->{'biblionumber'} = $biblionumber;
248 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
249 $sth->execute( $item->{'biblionumber'} );
250 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
252 _set_defaults_for_add($item);
253 _set_derived_columns_for_add($item);
254 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
255 # FIXME - checks here
256 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
257 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
258 $itype_sth->execute( $item->{'biblionumber'} );
259 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
262 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
263 $item->{'itemnumber'} = $itemnumber;
265 # create MARC tag representing item and add to bib
266 #my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
267 #_add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
268 ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver", undef, undef );
270 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
272 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
275 =head2 AddItemBatchFromMarc
277 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record,
278 $biblionumber, $biblioitemnumber, $frameworkcode);
280 Efficiently create item records from a MARC biblio record with
281 embedded item fields. This routine is suitable for batch jobs.
283 This API assumes that the bib record has already been
284 saved to the C<biblio> and C<biblioitems> tables. It does
285 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
286 are populated, but it will do so via a call to ModBibiloMarc.
288 The goal of this API is to have a similar effect to using AddBiblio
289 and AddItems in succession, but without inefficient repeated
290 parsing of the MARC XML bib record.
292 This function returns an arrayref of new itemsnumbers and an arrayref of item
293 errors encountered during the processing. Each entry in the errors
294 list is a hashref containing the following keys:
300 Sequence number of original item tag in the MARC record.
304 Item barcode, provide to assist in the construction of
305 useful error messages.
307 =item error_condition
309 Code representing the error condition. Can be 'duplicate_barcode',
310 'invalid_homebranch', or 'invalid_holdingbranch'.
312 =item error_information
314 Additional information appropriate to the error condition.
320 sub AddItemBatchFromMarc {
321 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
323 my @itemnumbers = ();
325 my $dbh = C4::Context->dbh;
327 # loop through the item tags and start creating items
328 my @bad_item_fields = ();
329 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
330 my $item_sequence_num = 0;
331 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
332 $item_sequence_num++;
333 # we take the item field and stick it into a new
334 # MARC record -- this is required so far because (FIXME)
335 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
336 # and there is no TransformMarcFieldToKoha
337 my $temp_item_marc = MARC::Record->new();
338 $temp_item_marc->append_fields($item_field);
340 # add biblionumber and biblioitemnumber
341 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
342 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
343 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
344 $item->{'biblionumber'} = $biblionumber;
345 $item->{'biblioitemnumber'} = $biblioitemnumber;
347 # check for duplicate barcode
348 my %item_errors = CheckItemPreSave($item);
350 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
351 push @bad_item_fields, $item_field;
355 _set_defaults_for_add($item);
356 _set_derived_columns_for_add($item);
357 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
358 warn $error if $error;
359 push @itemnumbers, $itemnumber; # FIXME not checking error
360 $item->{'itemnumber'} = $itemnumber;
362 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
364 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
365 $item_field->replace_with($new_item_marc->field($itemtag));
368 # remove any MARC item fields for rejected items
369 foreach my $item_field (@bad_item_fields) {
370 $record->delete_field($item_field);
373 # update the MARC biblio
374 # $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
376 return (\@itemnumbers, \@errors);
379 =head2 ModItemFromMarc
381 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
383 This function updates an item record based on a supplied
384 C<MARC::Record> object containing an embedded item field.
385 This API is meant for the use of C<additem.pl>; for
386 other purposes, C<ModItem> should be used.
388 This function uses the hash %default_values_for_mod_from_marc,
389 which contains default values for item fields to
390 apply when modifying an item. This is needed beccause
391 if an item field's value is cleared, TransformMarcToKoha
392 does not include the column in the
393 hash that's passed to ModItem, which without
394 use of this hash makes it impossible to clear
395 an item field's value. See bug 2466.
397 Note that only columns that can be directly
398 changed from the cataloging and serials
399 item editors are included in this hash.
403 my %default_values_for_mod_from_marc = (
405 booksellerid => undef,
407 'items.cn_source' => undef,
410 # dateaccessioned => undef,
412 holdingbranch => undef,
414 itemcallnumber => undef,
423 replacementprice => undef,
424 replacementpricedate => undef,
427 stocknumber => undef,
432 sub ModItemFromMarc {
433 my $item_marc = shift;
434 my $biblionumber = shift;
435 my $itemnumber = shift;
437 my $dbh = C4::Context->dbh;
438 my $frameworkcode = GetFrameworkCode($biblionumber);
439 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
441 my $localitemmarc = MARC::Record->new;
442 $localitemmarc->append_fields( $item_marc->field($itemtag) );
443 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
444 foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
445 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
447 my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
449 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
454 ModItem({ column => $newvalue }, $biblionumber,
455 $itemnumber[, $original_item_marc]);
457 Change one or more columns in an item record and update
458 the MARC representation of the item.
460 The first argument is a hashref mapping from item column
461 names to the new values. The second and third arguments
462 are the biblionumber and itemnumber, respectively.
464 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
465 an arrayref containing subfields present in the original MARC
466 representation of the item (e.g., from the item editor) that are
467 not mapped to C<items> columns directly but should instead
468 be stored in C<items.more_subfields_xml> and included in
469 the biblio items tag for display and indexing.
471 If one of the changed columns is used to calculate
472 the derived value of a column such as C<items.cn_sort>,
473 this routine will perform the necessary calculation
480 my $biblionumber = shift;
481 my $itemnumber = shift;
483 # if $biblionumber is undefined, get it from the current item
484 unless (defined $biblionumber) {
485 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
488 my $dbh = @_ ? shift : C4::Context->dbh;
489 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
491 my $unlinked_item_subfields;
493 $unlinked_item_subfields = shift;
494 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
497 $item->{'itemnumber'} = $itemnumber or return undef;
498 _set_derived_columns_for_mod($item);
499 _do_column_fixes_for_mod($item);
502 # attempt to change itemnumber
503 # attempt to change biblionumber (if we want
504 # an API to relink an item to a different bib,
505 # it should be a separate function)
508 _koha_modify_item($item);
510 # update biblio MARC XML
511 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
512 ModZebra( $whole_item->{biblionumber}, "specialUpdate", "biblioserver", undef, undef );
514 logaction("CATALOGUING", "MODIFY", $itemnumber, Dumper($item)) if C4::Context->preference("CataloguingLog");
517 =head2 ModItemTransfer
519 ModItemTransfer($itenumber, $frombranch, $tobranch);
521 Marks an item as being transferred from one branch
526 sub ModItemTransfer {
527 my ( $itemnumber, $frombranch, $tobranch ) = @_;
529 my $dbh = C4::Context->dbh;
531 #new entry in branchtransfers....
532 my $sth = $dbh->prepare(
533 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
534 VALUES (?, ?, NOW(), ?)");
535 $sth->execute($itemnumber, $frombranch, $tobranch);
537 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
538 ModDateLastSeen($itemnumber);
542 =head2 ModDateLastSeen
544 ModDateLastSeen($itemnum);
546 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
547 C<$itemnum> is the item number
551 sub ModDateLastSeen {
552 my ($itemnumber) = @_;
554 my $today = C4::Dates->new();
555 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
560 DelItem($dbh, $biblionumber, $itemnumber);
562 Exported function (core API) for deleting an item record in Koha.
567 my ( $dbh, $biblionumber, $itemnumber ) = @_;
569 # FIXME check the item has no current issues
571 _koha_delete_item( $dbh, $itemnumber );
573 # get the MARC record
574 my $record = GetMarcBiblio($biblionumber);
575 ModZebra( $biblionumber, "specialUpdate", "biblioserver", undef, undef );
578 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
579 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
581 #search item field code
582 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
585 =head2 CheckItemPreSave
587 my $item_ref = TransformMarcToKoha($marc, 'items');
589 my %errors = CheckItemPreSave($item_ref);
590 if (exists $errors{'duplicate_barcode'}) {
591 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
592 } elsif (exists $errors{'invalid_homebranch'}) {
593 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
594 } elsif (exists $errors{'invalid_holdingbranch'}) {
595 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
600 Given a hashref containing item fields, determine if it can be
601 inserted or updated in the database. Specifically, checks for
602 database integrity issues, and returns a hash containing any
603 of the following keys, if applicable.
607 =item duplicate_barcode
609 Barcode, if it duplicates one already found in the database.
611 =item invalid_homebranch
613 Home branch, if not defined in branches table.
615 =item invalid_holdingbranch
617 Holding branch, if not defined in branches table.
621 This function does NOT implement any policy-related checks,
622 e.g., whether current operator is allowed to save an
623 item that has a given branch code.
627 sub CheckItemPreSave {
628 my $item_ref = shift;
632 # check for duplicate barcode
633 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
634 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
635 if ($existing_itemnumber) {
636 if (!exists $item_ref->{'itemnumber'} # new item
637 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
638 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
643 # check for valid home branch
644 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
645 my $branch_name = GetBranchName($item_ref->{'homebranch'});
646 unless (defined $branch_name) {
647 # relies on fact that branches.branchname is a non-NULL column,
648 # so GetBranchName returns undef only if branch does not exist
649 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
653 # check for valid holding branch
654 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
655 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
656 unless (defined $branch_name) {
657 # relies on fact that branches.branchname is a non-NULL column,
658 # so GetBranchName returns undef only if branch does not exist
659 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
667 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
669 The following functions provide various ways of
670 getting an item record, a set of item records, or
671 lists of authorized values for certain item fields.
673 Some of the functions in this group are candidates
674 for refactoring -- for example, some of the code
675 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
676 has copy-and-paste work.
682 $itemstatushash = GetItemStatus($fwkcode);
684 Returns a list of valid values for the
685 C<items.notforloan> field.
687 NOTE: does B<not> return an individual item's
690 Can be MARC dependant.
692 But basically could be can be loan or not
693 Create a status selector with the following code
695 =head3 in PERL SCRIPT
697 my $itemstatushash = getitemstatus;
699 foreach my $thisstatus (keys %$itemstatushash) {
700 my %row =(value => $thisstatus,
701 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
703 push @itemstatusloop, \%row;
705 $template->param(statusloop=>\@itemstatusloop);
709 <select name="statusloop">
710 <option value="">Default</option>
711 <!-- TMPL_LOOP name="statusloop" -->
712 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
720 # returns a reference to a hash of references to status...
723 my $dbh = C4::Context->dbh;
725 $fwk = '' unless ($fwk);
726 my ( $tag, $subfield ) =
727 GetMarcFromKohaField( "items.notforloan", $fwk );
728 if ( $tag and $subfield ) {
731 "SELECT authorised_value
732 FROM marc_subfield_structure
738 $sth->execute( $tag, $subfield, $fwk );
739 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
742 "SELECT authorised_value,lib
743 FROM authorised_values
748 $authvalsth->execute($authorisedvaluecat);
749 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
750 $itemstatus{$authorisedvalue} = $lib;
764 $itemstatus{"1"} = "Not For Loan";
768 =head2 GetItemLocation
770 $itemlochash = GetItemLocation($fwk);
772 Returns a list of valid values for the
773 C<items.location> field.
775 NOTE: does B<not> return an individual item's
778 where fwk stands for an optional framework code.
779 Create a location selector with the following code
781 =head3 in PERL SCRIPT
783 my $itemlochash = getitemlocation;
785 foreach my $thisloc (keys %$itemlochash) {
786 my $selected = 1 if $thisbranch eq $branch;
787 my %row =(locval => $thisloc,
788 selected => $selected,
789 locname => $itemlochash->{$thisloc},
791 push @itemlocloop, \%row;
793 $template->param(itemlocationloop => \@itemlocloop);
797 <select name="location">
798 <option value="">Default</option>
799 <!-- TMPL_LOOP name="itemlocationloop" -->
800 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
806 sub GetItemLocation {
808 # returns a reference to a hash of references to location...
811 my $dbh = C4::Context->dbh;
813 $fwk = '' unless ($fwk);
814 my ( $tag, $subfield ) =
815 GetMarcFromKohaField( "items.location", $fwk );
816 if ( $tag and $subfield ) {
819 "SELECT authorised_value
820 FROM marc_subfield_structure
825 $sth->execute( $tag, $subfield, $fwk );
826 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
829 "SELECT authorised_value,lib
830 FROM authorised_values
834 $authvalsth->execute($authorisedvaluecat);
835 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
836 $itemlocation{$authorisedvalue} = $lib;
838 return \%itemlocation;
850 $itemlocation{"1"} = "Not For Loan";
851 return \%itemlocation;
856 $items = GetLostItems( $where, $orderby );
858 This function gets a list of lost items.
864 C<$where> is a hashref. it containts a field of the items table as key
865 and the value to match as value. For example:
867 { barcode => 'abc123',
868 homebranch => 'CPL', }
870 C<$orderby> is a field of the items table by which the resultset
875 C<$items> is a reference to an array full of hashrefs with columns
876 from the "items" table as keys.
878 =item usage in the perl script:
880 my $where = { barcode => '0001548' };
881 my $items = GetLostItems( $where, "homebranch" );
882 $template->param( itemsloop => $items );
889 # Getting input args.
892 my $dbh = C4::Context->dbh;
897 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
898 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
899 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
901 authorised_values.category = 'LOST'
902 AND itemlost IS NOT NULL
905 my @query_parameters;
906 foreach my $key (keys %$where) {
907 $query .= " AND $key LIKE ?";
908 push @query_parameters, "%$where->{$key}%";
910 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
912 if ( defined $orderby && grep($orderby, @ordervalues)) {
913 $query .= ' ORDER BY '.$orderby;
916 my $sth = $dbh->prepare($query);
917 $sth->execute( @query_parameters );
919 while ( my $row = $sth->fetchrow_hashref ){
925 =head2 GetItemsForInventory
927 $itemlist = GetItemsForInventory($minlocation, $maxlocation,
928 $location, $itemtype $datelastseen, $branch,
929 $offset, $size, $statushash);
931 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
933 The sub returns a reference to a list of hashes, each containing
934 itemnumber, author, title, barcode, item callnumber, and date last
935 seen. It is ordered by callnumber then title.
937 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
938 the datelastseen can be used to specify that you want to see items not seen since a past date only.
939 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
940 $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.
944 sub GetItemsForInventory {
945 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
946 my $dbh = C4::Context->dbh;
947 my ( @bind_params, @where_strings );
949 my $query = <<'END_SQL';
950 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
952 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
953 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
956 for my $authvfield (keys %$statushash){
957 if ( scalar @{$statushash->{$authvfield}} > 0 ){
958 my $joinedvals = join ',', @{$statushash->{$authvfield}};
959 push @where_strings, "$authvfield in (" . $joinedvals . ")";
965 push @where_strings, 'itemcallnumber >= ?';
966 push @bind_params, $minlocation;
970 push @where_strings, 'itemcallnumber <= ?';
971 push @bind_params, $maxlocation;
975 $datelastseen = format_date_in_iso($datelastseen);
976 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
977 push @bind_params, $datelastseen;
981 push @where_strings, 'items.location = ?';
982 push @bind_params, $location;
986 if($branch eq "homebranch"){
987 push @where_strings, 'items.homebranch = ?';
989 push @where_strings, 'items.holdingbranch = ?';
991 push @bind_params, $branchcode;
995 push @where_strings, 'biblioitems.itemtype = ?';
996 push @bind_params, $itemtype;
999 if ( $ignoreissued) {
1000 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1001 push @where_strings, 'issues.date_due IS NULL';
1004 if ( @where_strings ) {
1006 $query .= join ' AND ', @where_strings;
1008 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1009 my $sth = $dbh->prepare($query);
1010 $sth->execute( @bind_params );
1014 while ( my $row = $sth->fetchrow_hashref ) {
1015 $offset-- if ($offset);
1016 $row->{datelastseen}=format_date($row->{datelastseen});
1017 if ( ( !$offset ) && $size ) {
1018 push @results, $row;
1025 =head2 GetItemsCount
1027 $count = &GetItemsCount( $biblionumber);
1029 This function return count of item with $biblionumber
1034 my ( $biblionumber ) = @_;
1035 my $dbh = C4::Context->dbh;
1036 my $query = "SELECT count(*)
1038 WHERE biblionumber=?";
1039 my $sth = $dbh->prepare($query);
1040 $sth->execute($biblionumber);
1041 my $count = $sth->fetchrow;
1045 =head2 GetItemInfosOf
1047 GetItemInfosOf(@itemnumbers);
1051 sub GetItemInfosOf {
1052 my @itemnumbers = @_;
1057 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1059 return get_infos_of( $query, 'itemnumber' );
1062 =head2 GetItemsByBiblioitemnumber
1064 GetItemsByBiblioitemnumber($biblioitemnumber);
1066 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1067 Called by C<C4::XISBN>
1071 sub GetItemsByBiblioitemnumber {
1072 my ( $bibitem ) = @_;
1073 my $dbh = C4::Context->dbh;
1074 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1075 # Get all items attached to a biblioitem
1078 $sth->execute($bibitem) || die $sth->errstr;
1079 while ( my $data = $sth->fetchrow_hashref ) {
1080 # Foreach item, get circulation information
1081 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1082 WHERE itemnumber = ?
1083 AND issues.borrowernumber = borrowers.borrowernumber"
1085 $sth2->execute( $data->{'itemnumber'} );
1086 if ( my $data2 = $sth2->fetchrow_hashref ) {
1087 # if item is out, set the due date and who it is out too
1088 $data->{'date_due'} = $data2->{'date_due'};
1089 $data->{'cardnumber'} = $data2->{'cardnumber'};
1090 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1093 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1094 $data->{'date_due'} = '';
1096 # Find the last 3 people who borrowed this item.
1097 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1098 AND old_issues.borrowernumber = borrowers.borrowernumber
1099 ORDER BY returndate desc,timestamp desc LIMIT 3";
1100 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1101 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1103 while ( my $data2 = $sth2->fetchrow_hashref ) {
1104 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1105 $data->{"card$i2"} = $data2->{'cardnumber'};
1106 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1109 push(@results,$data);
1116 @results = GetItemsInfo($biblionumber, $type);
1118 Returns information about books with the given biblionumber.
1120 C<$type> may be either C<intra> or anything else. If it is not set to
1121 C<intra>, then the search will exclude lost, very overdue, and
1124 C<GetItemsInfo> returns a list of references-to-hash. Each element
1125 contains a number of keys. Most of them are table items from the
1126 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1127 Koha database. Other keys include:
1131 =item C<$data-E<gt>{branchname}>
1133 The name (not the code) of the branch to which the book belongs.
1135 =item C<$data-E<gt>{datelastseen}>
1137 This is simply C<items.datelastseen>, except that while the date is
1138 stored in YYYY-MM-DD format in the database, here it is converted to
1139 DD/MM/YYYY format. A NULL date is returned as C<//>.
1141 =item C<$data-E<gt>{datedue}>
1143 =item C<$data-E<gt>{class}>
1145 This is the concatenation of C<biblioitems.classification>, the book's
1146 Dewey code, and C<biblioitems.subclass>.
1148 =item C<$data-E<gt>{ocount}>
1150 I think this is the number of copies of the book available.
1152 =item C<$data-E<gt>{order}>
1154 If this is set, it is set to C<One Order>.
1161 my ( $biblionumber, $type ) = @_;
1162 my $dbh = C4::Context->dbh;
1163 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1169 biblioitems.itemtype,
1172 biblioitems.publicationyear,
1173 biblioitems.publishercode,
1174 biblioitems.volumedate,
1175 biblioitems.volumedesc,
1178 items.notforloan as itemnotforloan,
1179 itemtypes.description,
1180 itemtypes.notforloan as notforloan_per_itemtype,
1183 LEFT JOIN branches ON items.homebranch = branches.branchcode
1184 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1185 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1186 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1187 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1188 $query .= " WHERE items.biblionumber = ? ORDER BY branches.branchname,items.dateaccessioned desc" ;
1189 my $sth = $dbh->prepare($query);
1190 $sth->execute($biblionumber);
1195 my $isth = $dbh->prepare(
1196 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1197 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1198 WHERE itemnumber = ?"
1200 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1201 while ( my $data = $sth->fetchrow_hashref ) {
1204 $isth->execute( $data->{'itemnumber'} );
1205 if ( my $idata = $isth->fetchrow_hashref ) {
1206 $data->{borrowernumber} = $idata->{borrowernumber};
1207 $data->{cardnumber} = $idata->{cardnumber};
1208 $data->{surname} = $idata->{surname};
1209 $data->{firstname} = $idata->{firstname};
1210 $datedue = $idata->{'date_due'};
1211 if (C4::Context->preference("IndependantBranches")){
1212 my $userenv = C4::Context->userenv;
1213 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1214 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1218 if ( $data->{'serial'}) {
1219 $ssth->execute($data->{'itemnumber'}) ;
1220 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1223 if ( $datedue eq '' ) {
1224 my ( $restype, $reserves ) =
1225 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1226 # Previous conditional check with if ($restype) is not needed because a true
1227 # result for one item will result in subsequent items defaulting to this true
1229 $count_reserves = $restype;
1231 #get branch information.....
1232 my $bsth = $dbh->prepare(
1233 "SELECT * FROM branches WHERE branchcode = ?
1236 $bsth->execute( $data->{'holdingbranch'} );
1237 if ( my $bdata = $bsth->fetchrow_hashref ) {
1238 $data->{'branchname'} = $bdata->{'branchname'};
1240 $data->{'datedue'} = $datedue;
1241 $data->{'count_reserves'} = $count_reserves;
1243 # get notforloan complete status if applicable
1244 my $sthnflstatus = $dbh->prepare(
1245 'SELECT authorised_value
1246 FROM marc_subfield_structure
1247 WHERE kohafield="items.notforloan"
1251 $sthnflstatus->execute;
1252 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1253 if ($authorised_valuecode) {
1254 $sthnflstatus = $dbh->prepare(
1255 "SELECT lib FROM authorised_values
1257 AND authorised_value=?"
1259 $sthnflstatus->execute( $authorised_valuecode,
1260 $data->{itemnotforloan} );
1261 my ($lib) = $sthnflstatus->fetchrow;
1262 $data->{notforloanvalue} = $lib;
1265 # get restricted status and description if applicable
1266 my $restrictedstatus = $dbh->prepare(
1267 'SELECT authorised_value
1268 FROM marc_subfield_structure
1269 WHERE kohafield="items.restricted"
1273 $restrictedstatus->execute;
1274 ($authorised_valuecode) = $restrictedstatus->fetchrow;
1275 if ($authorised_valuecode) {
1276 $restrictedstatus = $dbh->prepare(
1277 "SELECT lib,lib_opac FROM authorised_values
1279 AND authorised_value=?"
1281 $restrictedstatus->execute( $authorised_valuecode,
1282 $data->{restricted} );
1284 if ( my $rstdata = $restrictedstatus->fetchrow_hashref ) {
1285 $data->{restricted} = $rstdata->{'lib'};
1286 $data->{restrictedopac} = $rstdata->{'lib_opac'};
1290 # my stack procedures
1291 my $stackstatus = $dbh->prepare(
1292 'SELECT authorised_value
1293 FROM marc_subfield_structure
1294 WHERE kohafield="items.stack"
1297 $stackstatus->execute;
1299 ($authorised_valuecode) = $stackstatus->fetchrow;
1300 if ($authorised_valuecode) {
1301 $stackstatus = $dbh->prepare(
1303 FROM authorised_values
1305 AND authorised_value=?
1308 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1309 my ($lib) = $stackstatus->fetchrow;
1310 $data->{stack} = $lib;
1312 # Find the last 3 people who borrowed this item.
1313 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1314 WHERE itemnumber = ?
1315 AND old_issues.borrowernumber = borrowers.borrowernumber
1316 ORDER BY returndate DESC
1318 $sth2->execute($data->{'itemnumber'});
1320 while (my $data2 = $sth2->fetchrow_hashref()) {
1321 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1322 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1323 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1327 $results[$i] = $data;
1331 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1337 =head2 GetItemsLocationInfo
1339 my @itemlocinfo = GetItemsLocationInfo($biblionumber);
1341 Returns the branch names, shelving location and itemcallnumber for each item attached to the biblio in question
1343 C<GetItemsInfo> returns a list of references-to-hash. Data returned:
1347 =item C<$data-E<gt>{homebranch}>
1349 Branch Name of the item's homebranch
1351 =item C<$data-E<gt>{holdingbranch}>
1353 Branch Name of the item's holdingbranch
1355 =item C<$data-E<gt>{location}>
1357 Item's shelving location code
1359 =item C<$data-E<gt>{location_intranet}>
1361 The intranet description for the Shelving Location as set in authorised_values 'LOC'
1363 =item C<$data-E<gt>{location_opac}>
1365 The OPAC description for the Shelving Location as set in authorised_values 'LOC'. Falls back to intranet description if no OPAC
1368 =item C<$data-E<gt>{itemcallnumber}>
1370 Item's itemcallnumber
1372 =item C<$data-E<gt>{cn_sort}>
1374 Item's call number normalized for sorting
1380 sub GetItemsLocationInfo {
1381 my $biblionumber = shift;
1384 my $dbh = C4::Context->dbh;
1385 my $query = "SELECT a.branchname as homebranch, b.branchname as holdingbranch,
1386 location, itemcallnumber, cn_sort
1387 FROM items, branches as a, branches as b
1388 WHERE homebranch = a.branchcode AND holdingbranch = b.branchcode
1389 AND biblionumber = ?
1390 ORDER BY cn_sort ASC";
1391 my $sth = $dbh->prepare($query);
1392 $sth->execute($biblionumber);
1394 while ( my $data = $sth->fetchrow_hashref ) {
1395 $data->{location_intranet} = GetKohaAuthorisedValueLib('LOC', $data->{location});
1396 $data->{location_opac}= GetKohaAuthorisedValueLib('LOC', $data->{location}, 1);
1397 push @results, $data;
1403 =head2 GetLastAcquisitions
1405 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'),
1406 'itemtypes' => ('BK','BD')}, 10);
1410 sub GetLastAcquisitions {
1411 my ($data,$max) = @_;
1413 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1415 my $number_of_branches = @{$data->{branches}};
1416 my $number_of_itemtypes = @{$data->{itemtypes}};
1419 my @where = ('WHERE 1 ');
1420 $number_of_branches and push @where
1421 , 'AND holdingbranch IN ('
1422 , join(',', ('?') x $number_of_branches )
1426 $number_of_itemtypes and push @where
1427 , "AND $itemtype IN ("
1428 , join(',', ('?') x $number_of_itemtypes )
1432 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1433 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1434 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1436 GROUP BY biblio.biblionumber
1437 ORDER BY dateaccessioned DESC LIMIT $max";
1439 my $dbh = C4::Context->dbh;
1440 my $sth = $dbh->prepare($query);
1442 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1445 while( my $row = $sth->fetchrow_hashref){
1446 push @results, {date => $row->{dateaccessioned}
1447 , biblionumber => $row->{biblionumber}
1448 , title => $row->{title}};
1454 =head2 get_itemnumbers_of
1456 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1458 Given a list of biblionumbers, return the list of corresponding itemnumbers
1459 for each biblionumber.
1461 Return a reference on a hash where keys are biblionumbers and values are
1462 references on array of itemnumbers.
1466 sub get_itemnumbers_of {
1467 my @biblionumbers = @_;
1469 my $dbh = C4::Context->dbh;
1475 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1477 my $sth = $dbh->prepare($query);
1478 $sth->execute(@biblionumbers);
1482 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1483 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1486 return \%itemnumbers_of;
1489 =head2 GetItemnumberFromBarcode
1491 $result = GetItemnumberFromBarcode($barcode);
1495 sub GetItemnumberFromBarcode {
1497 my $dbh = C4::Context->dbh;
1500 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1501 $rq->execute($barcode);
1502 my ($result) = $rq->fetchrow;
1506 =head2 GetBarcodeFromItemnumber
1508 $result = GetBarcodeFromItemnumber($itemnumber);
1512 sub GetBarcodeFromItemnumber {
1513 my ($itemnumber) = @_;
1514 my $dbh = C4::Context->dbh;
1517 $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1518 $rq->execute($itemnumber);
1519 my ($result) = $rq->fetchrow;
1523 =head2 GetHiddenItemnumbers
1527 $result = GetHiddenItemnumbers(@items);
1533 sub GetHiddenItemnumbers {
1537 my $yaml = C4::Context->preference('OpacHiddenItems');
1540 $hidingrules = YAML::Load($yaml);
1543 warn "Unable to parse OpacHiddenItems syspref : $@";
1546 my $dbh = C4::Context->dbh;
1549 foreach my $item (@items) {
1551 # We check each rule
1552 foreach my $field (keys %$hidingrules) {
1553 my $query = "SELECT $field from items where itemnumber = ?";
1554 my $sth = $dbh->prepare($query);
1555 $sth->execute($item->{'itemnumber'});
1556 my ($result) = $sth->fetchrow;
1558 # If the results matches the values in the yaml file
1559 if (any { $result eq $_ } @{$hidingrules->{$field}}) {
1561 # We add the itemnumber to the list
1562 push @resultitems, $item->{'itemnumber'};
1564 # If at least one rule matched for an item, no need to test the others
1569 return @resultitems;
1574 =head3 get_item_authorised_values
1576 find the types and values for all authorised values assigned to this item.
1578 parameters: itemnumber
1580 returns: a hashref malling the authorised value to the value set for this itemnumber
1582 $authorised_values = {
1588 'RESTRICTED' => undef,
1591 'branches' => 'CPL',
1592 'cn_source' => undef,
1593 'itemtypes' => 'SER',
1596 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1600 sub get_item_authorised_values {
1601 my $itemnumber = shift;
1603 # assume that these entries in the authorised_value table are item level.
1604 my $query = q(SELECT distinct authorised_value, kohafield
1605 FROM marc_subfield_structure
1606 WHERE kohafield like 'item%'
1607 AND authorised_value != '' );
1609 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1610 my $iteminfo = GetItem( $itemnumber );
1611 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1613 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1614 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1615 $field =~ s/^items\.//;
1616 if ( exists $iteminfo->{ $field } ) {
1617 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1620 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1624 =head3 get_authorised_value_images
1626 find a list of icons that are appropriate for display based on the
1627 authorised values for a biblio.
1629 parameters: listref of authorised values, such as comes from
1630 get_item_authorised_values or
1631 from C4::Biblio::get_biblio_authorised_values
1633 returns: listref of hashrefs for each image. Each hashref looks like this:
1635 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1640 Notes: Currently, I put on the full path to the images on the staff
1641 side. This should either be configurable or not done at all. Since I
1642 have to deal with 'intranet' or 'opac' in
1643 get_biblio_authorised_values, perhaps I should be passing it in.
1647 sub get_authorised_value_images {
1648 my $authorised_values = shift;
1652 my $authorised_value_list = GetAuthorisedValues();
1653 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1654 foreach my $this_authorised_value ( @$authorised_value_list ) {
1655 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1656 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1657 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1658 if ( defined $this_authorised_value->{'imageurl'} ) {
1659 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1660 label => $this_authorised_value->{'lib'},
1661 category => $this_authorised_value->{'category'},
1662 value => $this_authorised_value->{'authorised_value'}, };
1667 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1672 =head1 LIMITED USE FUNCTIONS
1674 The following functions, while part of the public API,
1675 are not exported. This is generally because they are
1676 meant to be used by only one script for a specific
1677 purpose, and should not be used in any other context
1678 without careful thought.
1684 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1686 Returns MARC::Record of the item passed in parameter.
1687 This function is meant for use only in C<cataloguing/additem.pl>,
1688 where it is needed to support that script's MARC-like
1694 my ( $biblionumber, $itemnumber ) = @_;
1696 # GetMarcItem has been revised so that it does the following:
1697 # 1. Gets the item information from the items table.
1698 # 2. Converts it to a MARC field for storage in the bib record.
1700 # The previous behavior was:
1701 # 1. Get the bib record.
1702 # 2. Return the MARC tag corresponding to the item record.
1704 # The difference is that one treats the items row as authoritative,
1705 # while the other treats the MARC representation as authoritative
1706 # under certain circumstances.
1708 my $itemrecord = GetItem($itemnumber);
1710 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1711 # Also, don't emit a subfield if the underlying field is blank.
1714 return Item2Marc($itemrecord,$biblionumber);
1718 my ($itemrecord,$biblionumber)=@_;
1721 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1722 } keys %{ $itemrecord }
1724 my $itemmarc = TransformKohaToMarc($mungeditem);
1725 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1727 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1728 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1729 foreach my $field ($itemmarc->field($itemtag)){
1730 $field->add_subfields(@$unlinked_item_subfields);
1736 =head1 PRIVATE FUNCTIONS AND VARIABLES
1738 The following functions are not meant to be called
1739 directly, but are documented in order to explain
1740 the inner workings of C<C4::Items>.
1744 =head2 %derived_columns
1746 This hash keeps track of item columns that
1747 are strictly derived from other columns in
1748 the item record and are not meant to be set
1751 Each key in the hash should be the name of a
1752 column (as named by TransformMarcToKoha). Each
1753 value should be hashref whose keys are the
1754 columns on which the derived column depends. The
1755 hashref should also contain a 'BUILDER' key
1756 that is a reference to a sub that calculates
1761 my %derived_columns = (
1762 'items.cn_sort' => {
1763 'itemcallnumber' => 1,
1764 'items.cn_source' => 1,
1765 'BUILDER' => \&_calc_items_cn_sort,
1769 =head2 _set_derived_columns_for_add
1771 _set_derived_column_for_add($item);
1773 Given an item hash representing a new item to be added,
1774 calculate any derived columns. Currently the only
1775 such column is C<items.cn_sort>.
1779 sub _set_derived_columns_for_add {
1782 foreach my $column (keys %derived_columns) {
1783 my $builder = $derived_columns{$column}->{'BUILDER'};
1784 my $source_values = {};
1785 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1786 next if $source_column eq 'BUILDER';
1787 $source_values->{$source_column} = $item->{$source_column};
1789 $builder->($item, $source_values);
1793 =head2 _set_derived_columns_for_mod
1795 _set_derived_column_for_mod($item);
1797 Given an item hash representing a new item to be modified.
1798 calculate any derived columns. Currently the only
1799 such column is C<items.cn_sort>.
1801 This routine differs from C<_set_derived_columns_for_add>
1802 in that it needs to handle partial item records. In other
1803 words, the caller of C<ModItem> may have supplied only one
1804 or two columns to be changed, so this function needs to
1805 determine whether any of the columns to be changed affect
1806 any of the derived columns. Also, if a derived column
1807 depends on more than one column, but the caller is not
1808 changing all of then, this routine retrieves the unchanged
1809 values from the database in order to ensure a correct
1814 sub _set_derived_columns_for_mod {
1817 foreach my $column (keys %derived_columns) {
1818 my $builder = $derived_columns{$column}->{'BUILDER'};
1819 my $source_values = {};
1820 my %missing_sources = ();
1821 my $must_recalc = 0;
1822 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1823 next if $source_column eq 'BUILDER';
1824 if (exists $item->{$source_column}) {
1826 $source_values->{$source_column} = $item->{$source_column};
1828 $missing_sources{$source_column} = 1;
1832 foreach my $source_column (keys %missing_sources) {
1833 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1835 $builder->($item, $source_values);
1840 =head2 _do_column_fixes_for_mod
1842 _do_column_fixes_for_mod($item);
1844 Given an item hashref containing one or more
1845 columns to modify, fix up certain values.
1846 Specifically, set to 0 any passed value
1847 of C<notforloan>, C<damaged>, C<itemlost>, or
1848 C<wthdrawn> that is either undefined or
1849 contains the empty string.
1853 sub _do_column_fixes_for_mod {
1856 if (exists $item->{'notforloan'} and
1857 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1858 $item->{'notforloan'} = 0;
1860 if (exists $item->{'damaged'} and
1861 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1862 $item->{'damaged'} = 0;
1864 if (exists $item->{'itemlost'} and
1865 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1866 $item->{'itemlost'} = 0;
1868 if (exists $item->{'wthdrawn'} and
1869 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1870 $item->{'wthdrawn'} = 0;
1872 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1873 $item->{'permanent_location'} = $item->{'location'};
1875 if (exists $item->{'timestamp'}) {
1876 delete $item->{'timestamp'};
1880 =head2 _get_single_item_column
1882 _get_single_item_column($column, $itemnumber);
1884 Retrieves the value of a single column from an C<items>
1885 row specified by C<$itemnumber>.
1889 sub _get_single_item_column {
1891 my $itemnumber = shift;
1893 my $dbh = C4::Context->dbh;
1894 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1895 $sth->execute($itemnumber);
1896 my ($value) = $sth->fetchrow();
1900 =head2 _calc_items_cn_sort
1902 _calc_items_cn_sort($item, $source_values);
1904 Helper routine to calculate C<items.cn_sort>.
1908 sub _calc_items_cn_sort {
1910 my $source_values = shift;
1912 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1915 =head2 _set_defaults_for_add
1917 _set_defaults_for_add($item_hash);
1919 Given an item hash representing an item to be added, set
1920 correct default values for columns whose default value
1921 is not handled by the DBMS. This includes the following
1928 C<items.dateaccessioned>
1950 sub _set_defaults_for_add {
1952 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1953 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1956 =head2 _koha_new_item
1958 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1960 Perform the actual insert into the C<items> table.
1964 sub _koha_new_item {
1965 my ( $item, $barcode ) = @_;
1966 my $dbh=C4::Context->dbh;
1969 "INSERT INTO items SET
1971 biblioitemnumber = ?,
1973 dateaccessioned = ?,
1977 replacementprice = ?,
1978 replacementpricedate = NOW(),
1979 datelastborrowed = ?,
1980 datelastseen = NOW(),
2003 more_subfields_xml = ?,
2007 my $sth = $dbh->prepare($query);
2009 $item->{'biblionumber'},
2010 $item->{'biblioitemnumber'},
2012 $item->{'dateaccessioned'},
2013 $item->{'booksellerid'},
2014 $item->{'homebranch'},
2016 $item->{'replacementprice'},
2017 $item->{datelastborrowed},
2019 $item->{'notforloan'},
2021 $item->{'itemlost'},
2022 $item->{'wthdrawn'},
2023 $item->{'itemcallnumber'},
2024 $item->{'restricted'},
2025 $item->{'itemnotes'},
2026 $item->{'holdingbranch'},
2028 $item->{'location'},
2031 $item->{'renewals'},
2032 $item->{'reserves'},
2033 $item->{'items.cn_source'},
2034 $item->{'items.cn_sort'},
2037 $item->{'materials'},
2039 $item->{'enumchron'},
2040 $item->{'more_subfields_xml'},
2041 $item->{'copynumber'},
2042 $item->{'stocknumber'},
2044 my $itemnumber = $dbh->{'mysql_insertid'};
2045 if ( defined $sth->errstr ) {
2046 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2048 return ( $itemnumber, $error );
2051 =head2 MoveItemFromBiblio
2053 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2055 Moves an item from a biblio to another
2057 Returns undef if the move failed or the biblionumber of the destination record otherwise
2061 sub MoveItemFromBiblio {
2062 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2063 my $dbh = C4::Context->dbh;
2064 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2065 $sth->execute( $tobiblio );
2066 my ( $tobiblioitem ) = $sth->fetchrow();
2067 $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2068 my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2070 ModZebra( $tobiblio, "specialUpdate", "biblioserver", undef, undef );
2071 ModZebra( $frombiblio, "specialUpdate", "biblioserver", undef, undef );
2072 # Checking if the item we want to move is in an order
2073 my $order = GetOrderFromItemnumber($itemnumber);
2075 # Replacing the biblionumber within the order if necessary
2076 $order->{'biblionumber'} = $tobiblio;
2086 DelItemCheck($dbh, $biblionumber, $itemnumber);
2088 Exported function (core API) for deleting an item record in Koha if there no current issue.
2093 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2096 # check that there is no issue on this item before deletion.
2097 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2098 $sth->execute($itemnumber);
2100 my $item = GetItem($itemnumber);
2101 my $onloan = $sth->fetchrow;
2103 $error = "book_on_loan";
2105 elsif (C4::Context->preference("IndependantBranches") and (C4::Context->userenv->{branch} ne $item->{C4::Context->preference("HomeOrHoldingBranch")||'homebranch'})){
2106 $error = "not_same_branch";
2110 $error = "book_on_loan"
2113 # check it doesnt have a waiting reserve
2114 $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2115 $sth->execute($itemnumber);
2116 my $reserve=$sth->fetchrow;
2118 $error = "book_reserved";
2121 DelItem($dbh, $biblionumber, $itemnumber);
2129 =head2 _koha_modify_item
2131 my ($itemnumber,$error) =_koha_modify_item( $item );
2133 Perform the actual update of the C<items> row. Note that this
2134 routine accepts a hashref specifying the columns to update.
2138 sub _koha_modify_item {
2140 my $dbh=C4::Context->dbh;
2143 my $query = "UPDATE items SET ";
2145 for my $key ( keys %$item ) {
2147 push @bind, $item->{$key};
2150 $query .= " WHERE itemnumber=?";
2151 push @bind, $item->{'itemnumber'};
2152 my $sth = C4::Context->dbh->prepare($query);
2153 $sth->execute(@bind);
2154 if ( C4::Context->dbh->errstr ) {
2155 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2158 return ($item->{'itemnumber'},$error);
2161 =head2 _koha_delete_item
2163 _koha_delete_item( $dbh, $itemnum );
2165 Internal function to delete an item record from the koha tables
2169 sub _koha_delete_item {
2170 my ( $dbh, $itemnum ) = @_;
2172 # save the deleted item to deleteditems table
2173 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2174 $sth->execute($itemnum);
2175 my $data = $sth->fetchrow_hashref();
2176 my $query = "INSERT INTO deleteditems SET ";
2178 foreach my $key ( keys %$data ) {
2179 $query .= "$key = ?,";
2180 push( @bind, $data->{$key} );
2183 $sth = $dbh->prepare($query);
2184 $sth->execute(@bind);
2186 # delete from items table
2187 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2188 $sth->execute($itemnum);
2192 =head2 _marc_from_item_hash
2194 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2196 Given an item hash representing a complete item record,
2197 create a C<MARC::Record> object containing an embedded
2198 tag representing that item.
2200 The third, optional parameter C<$unlinked_item_subfields> is
2201 an arrayref of subfields (not mapped to C<items> fields per the
2202 framework) to be added to the MARC representation
2207 sub _marc_from_item_hash {
2209 my $frameworkcode = shift;
2210 my $unlinked_item_subfields;
2212 $unlinked_item_subfields = shift;
2215 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2216 # Also, don't emit a subfield if the underlying field is blank.
2217 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2218 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2219 : () } keys %{ $item } };
2221 my $item_marc = MARC::Record->new();
2222 foreach my $item_field ( keys %{$mungeditem} ) {
2223 my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2224 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2225 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2226 foreach my $value (@values){
2227 if ( my $field = $item_marc->field($tag) ) {
2228 $field->add_subfields( $subfield => $value );
2230 my $add_subfields = [];
2231 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2232 $add_subfields = $unlinked_item_subfields;
2234 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2242 =head2 _add_item_field_to_biblio
2244 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2246 Adds the fields from a MARC record containing the
2247 representation of a Koha item record to the MARC
2248 biblio record. The input C<$item_marc> record
2249 is expect to contain just one field, the embedded
2250 item information field.
2254 sub _add_item_field_to_biblio {
2255 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2257 my $biblio_marc = GetMarcBiblio($biblionumber);
2258 foreach my $field ($item_marc->fields()) {
2259 $biblio_marc->append_fields($field);
2262 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2265 =head2 _replace_item_field_in_biblio
2267 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2269 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2270 representation of the item, examine the biblio MARC
2271 for the corresponding tag for that item and
2272 replace it with the tag from C<$item_marc>.
2276 sub _replace_item_field_in_biblio {
2277 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2278 my $dbh = C4::Context->dbh;
2280 # get complete MARC record & replace the item field by the new one
2281 my $completeRecord = GetMarcBiblio($biblionumber);
2282 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2283 my $itemField = $ItemRecord->field($itemtag);
2284 my @items = $completeRecord->field($itemtag);
2287 if ($_->subfield($itemsubfield) eq $itemnumber) {
2288 $_->replace_with($itemField);
2294 # If we haven't found the matching field,
2295 # just add it. However, this means that
2296 # there is likely a bug.
2297 $completeRecord->append_fields($itemField);
2301 #ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2304 =head2 _repack_item_errors
2306 Add an error message hash generated by C<CheckItemPreSave>
2307 to a list of errors.
2311 sub _repack_item_errors {
2312 my $item_sequence_num = shift;
2313 my $item_ref = shift;
2314 my $error_ref = shift;
2316 my @repacked_errors = ();
2318 foreach my $error_code (sort keys %{ $error_ref }) {
2319 my $repacked_error = {};
2320 $repacked_error->{'item_sequence'} = $item_sequence_num;
2321 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2322 $repacked_error->{'error_code'} = $error_code;
2323 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2324 push @repacked_errors, $repacked_error;
2327 return @repacked_errors;
2330 =head2 _get_unlinked_item_subfields
2332 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2336 sub _get_unlinked_item_subfields {
2337 my $original_item_marc = shift;
2338 my $frameworkcode = shift;
2340 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2342 # assume that this record has only one field, and that that
2343 # field contains only the item information
2345 my @fields = $original_item_marc->fields();
2346 if ($#fields > -1) {
2347 my $field = $fields[0];
2348 my $tag = $field->tag();
2349 foreach my $subfield ($field->subfields()) {
2350 if (defined $subfield->[1] and
2351 $subfield->[1] ne '' and
2352 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2353 push @$subfields, $subfield->[0] => $subfield->[1];
2360 =head2 _get_unlinked_subfields_xml
2362 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2366 sub _get_unlinked_subfields_xml {
2367 my $unlinked_item_subfields = shift;
2370 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2371 my $marc = MARC::Record->new();
2372 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2373 # used in the framework
2374 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2375 $marc->encoding("UTF-8");
2376 $xml = $marc->as_xml("USMARC");
2382 =head2 _parse_unlinked_item_subfields_from_xml
2384 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2388 sub _parse_unlinked_item_subfields_from_xml {
2391 return unless defined $xml and $xml ne "";
2392 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2393 my $unlinked_subfields = [];
2394 my @fields = $marc->fields();
2395 if ($#fields > -1) {
2396 foreach my $subfield ($fields[0]->subfields()) {
2397 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2400 return $unlinked_subfields;