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/;
37 use vars qw($VERSION @ISA @EXPORT);
43 @ISA = qw( Exporter );
66 GetItemsByBiblioitemnumber
69 GetItemnumberFromBarcode
70 GetBarcodeFromItemnumber
81 C4::Items - item management functions
85 This module contains an API for manipulating item
86 records in Koha, and is used by cataloguing, circulation,
87 acquisitions, and serials management.
89 A Koha item record is stored in two places: the
90 items table and embedded in a MARC tag in the XML
91 version of the associated bib record in C<biblioitems.marcxml>.
92 This is done to allow the item information to be readily
93 indexed (e.g., by Zebra), but means that each item
94 modification transaction must keep the items table
95 and the MARC XML in sync at all times.
97 Consequently, all code that creates, modifies, or deletes
98 item records B<must> use an appropriate function from
99 C<C4::Items>. If no existing function is suitable, it is
100 better to add one to C<C4::Items> than to use add
101 one-off SQL statements to add or modify items.
103 The items table will be considered authoritative. In other
104 words, if there is ever a discrepancy between the items
105 table and the MARC XML, the items table should be considered
108 =head1 HISTORICAL NOTE
110 Most of the functions in C<C4::Items> were originally in
111 the C<C4::Biblio> module.
113 =head1 CORE EXPORTED FUNCTIONS
115 The following functions are meant for use by users
122 $item = GetItem($itemnumber,$barcode,$serial);
124 Return item information, for a given itemnumber or barcode.
125 The return value is a hashref mapping item column
126 names to values. If C<$serial> is true, include serial publication data.
131 my ($itemnumber,$barcode, $serial) = @_;
132 my $dbh = C4::Context->dbh;
135 my $sth = $dbh->prepare("
137 WHERE itemnumber = ?");
138 $sth->execute($itemnumber);
139 $data = $sth->fetchrow_hashref;
141 my $sth = $dbh->prepare("
145 $sth->execute($barcode);
146 $data = $sth->fetchrow_hashref;
149 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
150 $ssth->execute($data->{'itemnumber'}) ;
151 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
153 #if we don't have an items.itype, use biblioitems.itemtype.
154 if( ! $data->{'itype'} ) {
155 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
156 $sth->execute($data->{'biblionumber'});
157 ($data->{'itype'}) = $sth->fetchrow_array;
164 CartToShelf($itemnumber);
166 Set the current shelving location of the item record
167 to its stored permanent shelving location. This is
168 primarily used to indicate when an item whose current
169 location is a special processing ('PROC') or shelving cart
170 ('CART') location is back in the stacks.
175 my ( $itemnumber ) = @_;
177 unless ( $itemnumber ) {
178 croak "FAILED CartToShelf() - no itemnumber supplied";
181 my $item = GetItem($itemnumber);
182 $item->{location} = $item->{permanent_location};
183 ModItem($item, undef, $itemnumber);
186 =head2 AddItemFromMarc
188 my ($biblionumber, $biblioitemnumber, $itemnumber)
189 = AddItemFromMarc($source_item_marc, $biblionumber);
191 Given a MARC::Record object containing an embedded item
192 record and a biblionumber, create a new item record.
196 sub AddItemFromMarc {
197 my ( $source_item_marc, $biblionumber ) = @_;
198 my $dbh = C4::Context->dbh;
200 # parse item hash from MARC
201 my $frameworkcode = GetFrameworkCode( $biblionumber );
202 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
204 my $localitemmarc=MARC::Record->new;
205 $localitemmarc->append_fields($source_item_marc->field($itemtag));
206 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
207 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
208 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
213 my ($biblionumber, $biblioitemnumber, $itemnumber)
214 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
216 Given a hash containing item column names as keys,
217 create a new Koha item record.
219 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
220 do not need to be supplied for general use; they exist
221 simply to allow them to be picked up from AddItemFromMarc.
223 The final optional parameter, C<$unlinked_item_subfields>, contains
224 an arrayref containing subfields present in the original MARC
225 representation of the item (e.g., from the item editor) that are
226 not mapped to C<items> columns directly but should instead
227 be stored in C<items.more_subfields_xml> and included in
228 the biblio items tag for display and indexing.
234 my $biblionumber = shift;
236 my $dbh = @_ ? shift : C4::Context->dbh;
237 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
238 my $unlinked_item_subfields;
240 $unlinked_item_subfields = shift
243 # needs old biblionumber and biblioitemnumber
244 $item->{'biblionumber'} = $biblionumber;
245 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
246 $sth->execute( $item->{'biblionumber'} );
247 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
249 _set_defaults_for_add($item);
250 _set_derived_columns_for_add($item);
251 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
252 # FIXME - checks here
253 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
254 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
255 $itype_sth->execute( $item->{'biblionumber'} );
256 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
259 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
260 $item->{'itemnumber'} = $itemnumber;
262 # create MARC tag representing item and add to bib
263 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
264 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
266 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
268 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
271 =head2 AddItemBatchFromMarc
273 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record,
274 $biblionumber, $biblioitemnumber, $frameworkcode);
276 Efficiently create item records from a MARC biblio record with
277 embedded item fields. This routine is suitable for batch jobs.
279 This API assumes that the bib record has already been
280 saved to the C<biblio> and C<biblioitems> tables. It does
281 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
282 are populated, but it will do so via a call to ModBibiloMarc.
284 The goal of this API is to have a similar effect to using AddBiblio
285 and AddItems in succession, but without inefficient repeated
286 parsing of the MARC XML bib record.
288 This function returns an arrayref of new itemsnumbers and an arrayref of item
289 errors encountered during the processing. Each entry in the errors
290 list is a hashref containing the following keys:
296 Sequence number of original item tag in the MARC record.
300 Item barcode, provide to assist in the construction of
301 useful error messages.
303 =item error_condition
305 Code representing the error condition. Can be 'duplicate_barcode',
306 'invalid_homebranch', or 'invalid_holdingbranch'.
308 =item error_information
310 Additional information appropriate to the error condition.
316 sub AddItemBatchFromMarc {
317 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
319 my @itemnumbers = ();
321 my $dbh = C4::Context->dbh;
323 # loop through the item tags and start creating items
324 my @bad_item_fields = ();
325 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
326 my $item_sequence_num = 0;
327 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
328 $item_sequence_num++;
329 # we take the item field and stick it into a new
330 # MARC record -- this is required so far because (FIXME)
331 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
332 # and there is no TransformMarcFieldToKoha
333 my $temp_item_marc = MARC::Record->new();
334 $temp_item_marc->append_fields($item_field);
336 # add biblionumber and biblioitemnumber
337 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
338 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
339 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
340 $item->{'biblionumber'} = $biblionumber;
341 $item->{'biblioitemnumber'} = $biblioitemnumber;
343 # check for duplicate barcode
344 my %item_errors = CheckItemPreSave($item);
346 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
347 push @bad_item_fields, $item_field;
351 _set_defaults_for_add($item);
352 _set_derived_columns_for_add($item);
353 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
354 warn $error if $error;
355 push @itemnumbers, $itemnumber; # FIXME not checking error
356 $item->{'itemnumber'} = $itemnumber;
358 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
360 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
361 $item_field->replace_with($new_item_marc->field($itemtag));
364 # remove any MARC item fields for rejected items
365 foreach my $item_field (@bad_item_fields) {
366 $record->delete_field($item_field);
369 # update the MARC biblio
370 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
372 return (\@itemnumbers, \@errors);
375 =head2 ModItemFromMarc
377 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
379 This function updates an item record based on a supplied
380 C<MARC::Record> object containing an embedded item field.
381 This API is meant for the use of C<additem.pl>; for
382 other purposes, C<ModItem> should be used.
384 This function uses the hash %default_values_for_mod_from_marc,
385 which contains default values for item fields to
386 apply when modifying an item. This is needed beccause
387 if an item field's value is cleared, TransformMarcToKoha
388 does not include the column in the
389 hash that's passed to ModItem, which without
390 use of this hash makes it impossible to clear
391 an item field's value. See bug 2466.
393 Note that only columns that can be directly
394 changed from the cataloging and serials
395 item editors are included in this hash.
399 my %default_values_for_mod_from_marc = (
401 booksellerid => undef,
403 'items.cn_source' => undef,
406 # dateaccessioned => undef,
408 holdingbranch => undef,
410 itemcallnumber => undef,
419 replacementprice => undef,
420 replacementpricedate => undef,
423 stocknumber => undef,
428 sub ModItemFromMarc {
429 my $item_marc = shift;
430 my $biblionumber = shift;
431 my $itemnumber = shift;
433 my $dbh = C4::Context->dbh;
434 my $frameworkcode = GetFrameworkCode($biblionumber);
435 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
437 my $localitemmarc = MARC::Record->new;
438 $localitemmarc->append_fields( $item_marc->field($itemtag) );
439 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items' );
440 foreach my $item_field ( keys %default_values_for_mod_from_marc ) {
441 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless (exists $item->{$item_field});
443 my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
445 my $dbh = C4::Context->dbh;
446 my $frameworkcode = GetFrameworkCode( $biblionumber );
447 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
449 my $localitemmarc=MARC::Record->new;
450 $localitemmarc->append_fields($item_marc->field($itemtag));
451 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items');
452 foreach my $item_field (keys %default_values_for_mod_from_marc) {
453 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
455 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
457 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
462 ModItem({ column => $newvalue }, $biblionumber,
463 $itemnumber[, $original_item_marc]);
465 Change one or more columns in an item record and update
466 the MARC representation of the item.
468 The first argument is a hashref mapping from item column
469 names to the new values. The second and third arguments
470 are the biblionumber and itemnumber, respectively.
472 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
473 an arrayref containing subfields present in the original MARC
474 representation of the item (e.g., from the item editor) that are
475 not mapped to C<items> columns directly but should instead
476 be stored in C<items.more_subfields_xml> and included in
477 the biblio items tag for display and indexing.
479 If one of the changed columns is used to calculate
480 the derived value of a column such as C<items.cn_sort>,
481 this routine will perform the necessary calculation
488 my $biblionumber = shift;
489 my $itemnumber = shift;
491 # if $biblionumber is undefined, get it from the current item
492 unless (defined $biblionumber) {
493 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
496 my $dbh = @_ ? shift : C4::Context->dbh;
497 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
499 my $unlinked_item_subfields;
501 $unlinked_item_subfields = shift;
502 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
505 $item->{'itemnumber'} = $itemnumber or return undef;
506 _set_derived_columns_for_mod($item);
507 _do_column_fixes_for_mod($item);
510 # attempt to change itemnumber
511 # attempt to change biblionumber (if we want
512 # an API to relink an item to a different bib,
513 # it should be a separate function)
516 _koha_modify_item($item);
518 # update biblio MARC XML
519 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
521 unless (defined $unlinked_item_subfields) {
522 $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
524 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields)
525 or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
527 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
528 ($new_item_marc eq '0') and die "$new_item_marc is '0', not hashref"; # logaction line would crash anyway
529 logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
532 =head2 ModItemTransfer
534 ModItemTransfer($itenumber, $frombranch, $tobranch);
536 Marks an item as being transferred from one branch
541 sub ModItemTransfer {
542 my ( $itemnumber, $frombranch, $tobranch ) = @_;
544 my $dbh = C4::Context->dbh;
546 #new entry in branchtransfers....
547 my $sth = $dbh->prepare(
548 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
549 VALUES (?, ?, NOW(), ?)");
550 $sth->execute($itemnumber, $frombranch, $tobranch);
552 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
553 ModDateLastSeen($itemnumber);
557 =head2 ModDateLastSeen
559 ModDateLastSeen($itemnum);
561 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
562 C<$itemnum> is the item number
566 sub ModDateLastSeen {
567 my ($itemnumber) = @_;
569 my $today = C4::Dates->new();
570 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
575 DelItem($dbh, $biblionumber, $itemnumber);
577 Exported function (core API) for deleting an item record in Koha.
582 my ( $dbh, $biblionumber, $itemnumber ) = @_;
584 # FIXME check the item has no current issues
586 _koha_delete_item( $dbh, $itemnumber );
588 # get the MARC record
589 my $record = GetMarcBiblio($biblionumber);
590 my $frameworkcode = GetFrameworkCode($biblionumber);
593 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
594 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
596 #search item field code
597 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
598 my @fields = $record->field($itemtag);
600 # delete the item specified
601 foreach my $field (@fields) {
602 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
603 $record->delete_field($field);
606 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
607 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
610 =head2 CheckItemPreSave
612 my $item_ref = TransformMarcToKoha($marc, 'items');
614 my %errors = CheckItemPreSave($item_ref);
615 if (exists $errors{'duplicate_barcode'}) {
616 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
617 } elsif (exists $errors{'invalid_homebranch'}) {
618 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
619 } elsif (exists $errors{'invalid_holdingbranch'}) {
620 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
625 Given a hashref containing item fields, determine if it can be
626 inserted or updated in the database. Specifically, checks for
627 database integrity issues, and returns a hash containing any
628 of the following keys, if applicable.
632 =item duplicate_barcode
634 Barcode, if it duplicates one already found in the database.
636 =item invalid_homebranch
638 Home branch, if not defined in branches table.
640 =item invalid_holdingbranch
642 Holding branch, if not defined in branches table.
646 This function does NOT implement any policy-related checks,
647 e.g., whether current operator is allowed to save an
648 item that has a given branch code.
652 sub CheckItemPreSave {
653 my $item_ref = shift;
657 # check for duplicate barcode
658 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
659 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
660 if ($existing_itemnumber) {
661 if (!exists $item_ref->{'itemnumber'} # new item
662 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
663 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
668 # check for valid home branch
669 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
670 my $branch_name = GetBranchName($item_ref->{'homebranch'});
671 unless (defined $branch_name) {
672 # relies on fact that branches.branchname is a non-NULL column,
673 # so GetBranchName returns undef only if branch does not exist
674 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
678 # check for valid holding branch
679 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
680 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
681 unless (defined $branch_name) {
682 # relies on fact that branches.branchname is a non-NULL column,
683 # so GetBranchName returns undef only if branch does not exist
684 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
692 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
694 The following functions provide various ways of
695 getting an item record, a set of item records, or
696 lists of authorized values for certain item fields.
698 Some of the functions in this group are candidates
699 for refactoring -- for example, some of the code
700 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
701 has copy-and-paste work.
707 $itemstatushash = GetItemStatus($fwkcode);
709 Returns a list of valid values for the
710 C<items.notforloan> field.
712 NOTE: does B<not> return an individual item's
715 Can be MARC dependant.
717 But basically could be can be loan or not
718 Create a status selector with the following code
720 =head3 in PERL SCRIPT
722 my $itemstatushash = getitemstatus;
724 foreach my $thisstatus (keys %$itemstatushash) {
725 my %row =(value => $thisstatus,
726 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
728 push @itemstatusloop, \%row;
730 $template->param(statusloop=>\@itemstatusloop);
734 <select name="statusloop">
735 <option value="">Default</option>
736 <!-- TMPL_LOOP name="statusloop" -->
737 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
745 # returns a reference to a hash of references to status...
748 my $dbh = C4::Context->dbh;
750 $fwk = '' unless ($fwk);
751 my ( $tag, $subfield ) =
752 GetMarcFromKohaField( "items.notforloan", $fwk );
753 if ( $tag and $subfield ) {
756 "SELECT authorised_value
757 FROM marc_subfield_structure
763 $sth->execute( $tag, $subfield, $fwk );
764 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
767 "SELECT authorised_value,lib
768 FROM authorised_values
773 $authvalsth->execute($authorisedvaluecat);
774 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
775 $itemstatus{$authorisedvalue} = $lib;
789 $itemstatus{"1"} = "Not For Loan";
793 =head2 GetItemLocation
795 $itemlochash = GetItemLocation($fwk);
797 Returns a list of valid values for the
798 C<items.location> field.
800 NOTE: does B<not> return an individual item's
803 where fwk stands for an optional framework code.
804 Create a location selector with the following code
806 =head3 in PERL SCRIPT
808 my $itemlochash = getitemlocation;
810 foreach my $thisloc (keys %$itemlochash) {
811 my $selected = 1 if $thisbranch eq $branch;
812 my %row =(locval => $thisloc,
813 selected => $selected,
814 locname => $itemlochash->{$thisloc},
816 push @itemlocloop, \%row;
818 $template->param(itemlocationloop => \@itemlocloop);
822 <select name="location">
823 <option value="">Default</option>
824 <!-- TMPL_LOOP name="itemlocationloop" -->
825 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
831 sub GetItemLocation {
833 # returns a reference to a hash of references to location...
836 my $dbh = C4::Context->dbh;
838 $fwk = '' unless ($fwk);
839 my ( $tag, $subfield ) =
840 GetMarcFromKohaField( "items.location", $fwk );
841 if ( $tag and $subfield ) {
844 "SELECT authorised_value
845 FROM marc_subfield_structure
850 $sth->execute( $tag, $subfield, $fwk );
851 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
854 "SELECT authorised_value,lib
855 FROM authorised_values
859 $authvalsth->execute($authorisedvaluecat);
860 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
861 $itemlocation{$authorisedvalue} = $lib;
863 return \%itemlocation;
875 $itemlocation{"1"} = "Not For Loan";
876 return \%itemlocation;
881 $items = GetLostItems( $where, $orderby );
883 This function gets a list of lost items.
889 C<$where> is a hashref. it containts a field of the items table as key
890 and the value to match as value. For example:
892 { barcode => 'abc123',
893 homebranch => 'CPL', }
895 C<$orderby> is a field of the items table by which the resultset
900 C<$items> is a reference to an array full of hashrefs with columns
901 from the "items" table as keys.
903 =item usage in the perl script:
905 my $where = { barcode => '0001548' };
906 my $items = GetLostItems( $where, "homebranch" );
907 $template->param( itemsloop => $items );
914 # Getting input args.
917 my $dbh = C4::Context->dbh;
922 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
923 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
924 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
926 authorised_values.category = 'LOST'
927 AND itemlost IS NOT NULL
930 my @query_parameters;
931 foreach my $key (keys %$where) {
932 $query .= " AND $key LIKE ?";
933 push @query_parameters, "%$where->{$key}%";
935 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
937 if ( defined $orderby && grep($orderby, @ordervalues)) {
938 $query .= ' ORDER BY '.$orderby;
941 my $sth = $dbh->prepare($query);
942 $sth->execute( @query_parameters );
944 while ( my $row = $sth->fetchrow_hashref ){
950 =head2 GetItemsForInventory
952 $itemlist = GetItemsForInventory($minlocation, $maxlocation,
953 $location, $itemtype $datelastseen, $branch,
954 $offset, $size, $statushash);
956 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
958 The sub returns a reference to a list of hashes, each containing
959 itemnumber, author, title, barcode, item callnumber, and date last
960 seen. It is ordered by callnumber then title.
962 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
963 the datelastseen can be used to specify that you want to see items not seen since a past date only.
964 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
965 $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.
969 sub GetItemsForInventory {
970 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branchcode, $branch, $offset, $size, $statushash ) = @_;
971 my $dbh = C4::Context->dbh;
972 my ( @bind_params, @where_strings );
974 my $query = <<'END_SQL';
975 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
977 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
978 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
981 for my $authvfield (keys %$statushash){
982 if ( scalar @{$statushash->{$authvfield}} > 0 ){
983 my $joinedvals = join ',', @{$statushash->{$authvfield}};
984 push @where_strings, "$authvfield in (" . $joinedvals . ")";
990 push @where_strings, 'itemcallnumber >= ?';
991 push @bind_params, $minlocation;
995 push @where_strings, 'itemcallnumber <= ?';
996 push @bind_params, $maxlocation;
1000 $datelastseen = format_date_in_iso($datelastseen);
1001 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1002 push @bind_params, $datelastseen;
1006 push @where_strings, 'items.location = ?';
1007 push @bind_params, $location;
1010 if ( $branchcode ) {
1011 if($branch eq "homebranch"){
1012 push @where_strings, 'items.homebranch = ?';
1014 push @where_strings, 'items.holdingbranch = ?';
1016 push @bind_params, $branchcode;
1020 push @where_strings, 'biblioitems.itemtype = ?';
1021 push @bind_params, $itemtype;
1024 if ( $ignoreissued) {
1025 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1026 push @where_strings, 'issues.date_due IS NULL';
1029 if ( @where_strings ) {
1031 $query .= join ' AND ', @where_strings;
1033 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1034 my $sth = $dbh->prepare($query);
1035 $sth->execute( @bind_params );
1039 while ( my $row = $sth->fetchrow_hashref ) {
1040 $offset-- if ($offset);
1041 $row->{datelastseen}=format_date($row->{datelastseen});
1042 if ( ( !$offset ) && $size ) {
1043 push @results, $row;
1050 =head2 GetItemsCount
1052 $count = &GetItemsCount( $biblionumber);
1054 This function return count of item with $biblionumber
1059 my ( $biblionumber ) = @_;
1060 my $dbh = C4::Context->dbh;
1061 my $query = "SELECT count(*)
1063 WHERE biblionumber=?";
1064 my $sth = $dbh->prepare($query);
1065 $sth->execute($biblionumber);
1066 my $count = $sth->fetchrow;
1070 =head2 GetItemInfosOf
1072 GetItemInfosOf(@itemnumbers);
1076 sub GetItemInfosOf {
1077 my @itemnumbers = @_;
1082 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1084 return get_infos_of( $query, 'itemnumber' );
1087 =head2 GetItemsByBiblioitemnumber
1089 GetItemsByBiblioitemnumber($biblioitemnumber);
1091 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1092 Called by C<C4::XISBN>
1096 sub GetItemsByBiblioitemnumber {
1097 my ( $bibitem ) = @_;
1098 my $dbh = C4::Context->dbh;
1099 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1100 # Get all items attached to a biblioitem
1103 $sth->execute($bibitem) || die $sth->errstr;
1104 while ( my $data = $sth->fetchrow_hashref ) {
1105 # Foreach item, get circulation information
1106 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1107 WHERE itemnumber = ?
1108 AND issues.borrowernumber = borrowers.borrowernumber"
1110 $sth2->execute( $data->{'itemnumber'} );
1111 if ( my $data2 = $sth2->fetchrow_hashref ) {
1112 # if item is out, set the due date and who it is out too
1113 $data->{'date_due'} = $data2->{'date_due'};
1114 $data->{'cardnumber'} = $data2->{'cardnumber'};
1115 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1118 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1119 $data->{'date_due'} = '';
1121 # Find the last 3 people who borrowed this item.
1122 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1123 AND old_issues.borrowernumber = borrowers.borrowernumber
1124 ORDER BY returndate desc,timestamp desc LIMIT 3";
1125 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1126 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1128 while ( my $data2 = $sth2->fetchrow_hashref ) {
1129 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1130 $data->{"card$i2"} = $data2->{'cardnumber'};
1131 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1134 push(@results,$data);
1141 @results = GetItemsInfo($biblionumber, $type);
1143 Returns information about books with the given biblionumber.
1145 C<$type> may be either C<intra> or anything else. If it is not set to
1146 C<intra>, then the search will exclude lost, very overdue, and
1149 C<GetItemsInfo> returns a list of references-to-hash. Each element
1150 contains a number of keys. Most of them are table items from the
1151 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1152 Koha database. Other keys include:
1156 =item C<$data-E<gt>{branchname}>
1158 The name (not the code) of the branch to which the book belongs.
1160 =item C<$data-E<gt>{datelastseen}>
1162 This is simply C<items.datelastseen>, except that while the date is
1163 stored in YYYY-MM-DD format in the database, here it is converted to
1164 DD/MM/YYYY format. A NULL date is returned as C<//>.
1166 =item C<$data-E<gt>{datedue}>
1168 =item C<$data-E<gt>{class}>
1170 This is the concatenation of C<biblioitems.classification>, the book's
1171 Dewey code, and C<biblioitems.subclass>.
1173 =item C<$data-E<gt>{ocount}>
1175 I think this is the number of copies of the book available.
1177 =item C<$data-E<gt>{order}>
1179 If this is set, it is set to C<One Order>.
1186 my ( $biblionumber, $type ) = @_;
1187 my $dbh = C4::Context->dbh;
1188 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1194 biblioitems.itemtype,
1197 biblioitems.publicationyear,
1198 biblioitems.publishercode,
1199 biblioitems.volumedate,
1200 biblioitems.volumedesc,
1203 items.notforloan as itemnotforloan,
1204 itemtypes.description,
1205 itemtypes.notforloan as notforloan_per_itemtype,
1208 LEFT JOIN branches ON items.homebranch = branches.branchcode
1209 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1210 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1211 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1212 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1213 $query .= " WHERE items.biblionumber = ? ORDER BY branches.branchname,items.dateaccessioned desc" ;
1214 my $sth = $dbh->prepare($query);
1215 $sth->execute($biblionumber);
1220 my $isth = $dbh->prepare(
1221 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1222 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1223 WHERE itemnumber = ?"
1225 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1226 while ( my $data = $sth->fetchrow_hashref ) {
1229 $isth->execute( $data->{'itemnumber'} );
1230 if ( my $idata = $isth->fetchrow_hashref ) {
1231 $data->{borrowernumber} = $idata->{borrowernumber};
1232 $data->{cardnumber} = $idata->{cardnumber};
1233 $data->{surname} = $idata->{surname};
1234 $data->{firstname} = $idata->{firstname};
1235 $datedue = $idata->{'date_due'};
1236 if (C4::Context->preference("IndependantBranches")){
1237 my $userenv = C4::Context->userenv;
1238 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1239 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1243 if ( $data->{'serial'}) {
1244 $ssth->execute($data->{'itemnumber'}) ;
1245 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1248 if ( $datedue eq '' ) {
1249 my ( $restype, $reserves ) =
1250 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1251 # Previous conditional check with if ($restype) is not needed because a true
1252 # result for one item will result in subsequent items defaulting to this true
1254 $count_reserves = $restype;
1256 #get branch information.....
1257 my $bsth = $dbh->prepare(
1258 "SELECT * FROM branches WHERE branchcode = ?
1261 $bsth->execute( $data->{'holdingbranch'} );
1262 if ( my $bdata = $bsth->fetchrow_hashref ) {
1263 $data->{'branchname'} = $bdata->{'branchname'};
1265 $data->{'datedue'} = $datedue;
1266 $data->{'count_reserves'} = $count_reserves;
1268 # get notforloan complete status if applicable
1269 my $sthnflstatus = $dbh->prepare(
1270 'SELECT authorised_value
1271 FROM marc_subfield_structure
1272 WHERE kohafield="items.notforloan"
1276 $sthnflstatus->execute;
1277 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1278 if ($authorised_valuecode) {
1279 $sthnflstatus = $dbh->prepare(
1280 "SELECT lib FROM authorised_values
1282 AND authorised_value=?"
1284 $sthnflstatus->execute( $authorised_valuecode,
1285 $data->{itemnotforloan} );
1286 my ($lib) = $sthnflstatus->fetchrow;
1287 $data->{notforloanvalue} = $lib;
1290 # get restricted status and description if applicable
1291 my $restrictedstatus = $dbh->prepare(
1292 'SELECT authorised_value
1293 FROM marc_subfield_structure
1294 WHERE kohafield="items.restricted"
1298 $restrictedstatus->execute;
1299 ($authorised_valuecode) = $restrictedstatus->fetchrow;
1300 if ($authorised_valuecode) {
1301 $restrictedstatus = $dbh->prepare(
1302 "SELECT lib,lib_opac FROM authorised_values
1304 AND authorised_value=?"
1306 $restrictedstatus->execute( $authorised_valuecode,
1307 $data->{restricted} );
1309 if ( my $rstdata = $restrictedstatus->fetchrow_hashref ) {
1310 $data->{restricted} = $rstdata->{'lib'};
1311 $data->{restrictedopac} = $rstdata->{'lib_opac'};
1315 # my stack procedures
1316 my $stackstatus = $dbh->prepare(
1317 'SELECT authorised_value
1318 FROM marc_subfield_structure
1319 WHERE kohafield="items.stack"
1322 $stackstatus->execute;
1324 ($authorised_valuecode) = $stackstatus->fetchrow;
1325 if ($authorised_valuecode) {
1326 $stackstatus = $dbh->prepare(
1328 FROM authorised_values
1330 AND authorised_value=?
1333 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1334 my ($lib) = $stackstatus->fetchrow;
1335 $data->{stack} = $lib;
1337 # Find the last 3 people who borrowed this item.
1338 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1339 WHERE itemnumber = ?
1340 AND old_issues.borrowernumber = borrowers.borrowernumber
1341 ORDER BY returndate DESC
1343 $sth2->execute($data->{'itemnumber'});
1345 while (my $data2 = $sth2->fetchrow_hashref()) {
1346 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1347 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1348 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1352 $results[$i] = $data;
1356 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1362 =head2 GetLastAcquisitions
1364 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'),
1365 'itemtypes' => ('BK','BD')}, 10);
1369 sub GetLastAcquisitions {
1370 my ($data,$max) = @_;
1372 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1374 my $number_of_branches = @{$data->{branches}};
1375 my $number_of_itemtypes = @{$data->{itemtypes}};
1378 my @where = ('WHERE 1 ');
1379 $number_of_branches and push @where
1380 , 'AND holdingbranch IN ('
1381 , join(',', ('?') x $number_of_branches )
1385 $number_of_itemtypes and push @where
1386 , "AND $itemtype IN ("
1387 , join(',', ('?') x $number_of_itemtypes )
1391 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1392 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1393 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1395 GROUP BY biblio.biblionumber
1396 ORDER BY dateaccessioned DESC LIMIT $max";
1398 my $dbh = C4::Context->dbh;
1399 my $sth = $dbh->prepare($query);
1401 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1404 while( my $row = $sth->fetchrow_hashref){
1405 push @results, {date => $row->{dateaccessioned}
1406 , biblionumber => $row->{biblionumber}
1407 , title => $row->{title}};
1413 =head2 get_itemnumbers_of
1415 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1417 Given a list of biblionumbers, return the list of corresponding itemnumbers
1418 for each biblionumber.
1420 Return a reference on a hash where keys are biblionumbers and values are
1421 references on array of itemnumbers.
1425 sub get_itemnumbers_of {
1426 my @biblionumbers = @_;
1428 my $dbh = C4::Context->dbh;
1434 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1436 my $sth = $dbh->prepare($query);
1437 $sth->execute(@biblionumbers);
1441 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1442 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1445 return \%itemnumbers_of;
1448 =head2 GetItemnumberFromBarcode
1450 $result = GetItemnumberFromBarcode($barcode);
1454 sub GetItemnumberFromBarcode {
1456 my $dbh = C4::Context->dbh;
1459 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1460 $rq->execute($barcode);
1461 my ($result) = $rq->fetchrow;
1465 =head2 GetBarcodeFromItemnumber
1467 $result = GetBarcodeFromItemnumber($itemnumber);
1471 sub GetBarcodeFromItemnumber {
1472 my ($itemnumber) = @_;
1473 my $dbh = C4::Context->dbh;
1476 $dbh->prepare("SELECT barcode FROM items WHERE items.itemnumber=?");
1477 $rq->execute($itemnumber);
1478 my ($result) = $rq->fetchrow;
1482 =head3 get_item_authorised_values
1484 find the types and values for all authorised values assigned to this item.
1486 parameters: itemnumber
1488 returns: a hashref malling the authorised value to the value set for this itemnumber
1490 $authorised_values = {
1496 'RESTRICTED' => undef,
1499 'branches' => 'CPL',
1500 'cn_source' => undef,
1501 'itemtypes' => 'SER',
1504 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1508 sub get_item_authorised_values {
1509 my $itemnumber = shift;
1511 # assume that these entries in the authorised_value table are item level.
1512 my $query = q(SELECT distinct authorised_value, kohafield
1513 FROM marc_subfield_structure
1514 WHERE kohafield like 'item%'
1515 AND authorised_value != '' );
1517 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1518 my $iteminfo = GetItem( $itemnumber );
1519 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1521 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1522 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1523 $field =~ s/^items\.//;
1524 if ( exists $iteminfo->{ $field } ) {
1525 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1528 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1532 =head3 get_authorised_value_images
1534 find a list of icons that are appropriate for display based on the
1535 authorised values for a biblio.
1537 parameters: listref of authorised values, such as comes from
1538 get_item_authorised_values or
1539 from C4::Biblio::get_biblio_authorised_values
1541 returns: listref of hashrefs for each image. Each hashref looks like this:
1543 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1548 Notes: Currently, I put on the full path to the images on the staff
1549 side. This should either be configurable or not done at all. Since I
1550 have to deal with 'intranet' or 'opac' in
1551 get_biblio_authorised_values, perhaps I should be passing it in.
1555 sub get_authorised_value_images {
1556 my $authorised_values = shift;
1560 my $authorised_value_list = GetAuthorisedValues();
1561 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1562 foreach my $this_authorised_value ( @$authorised_value_list ) {
1563 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1564 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1565 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1566 if ( defined $this_authorised_value->{'imageurl'} ) {
1567 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1568 label => $this_authorised_value->{'lib'},
1569 category => $this_authorised_value->{'category'},
1570 value => $this_authorised_value->{'authorised_value'}, };
1575 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1580 =head1 LIMITED USE FUNCTIONS
1582 The following functions, while part of the public API,
1583 are not exported. This is generally because they are
1584 meant to be used by only one script for a specific
1585 purpose, and should not be used in any other context
1586 without careful thought.
1592 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1594 Returns MARC::Record of the item passed in parameter.
1595 This function is meant for use only in C<cataloguing/additem.pl>,
1596 where it is needed to support that script's MARC-like
1602 my ( $biblionumber, $itemnumber ) = @_;
1604 # GetMarcItem has been revised so that it does the following:
1605 # 1. Gets the item information from the items table.
1606 # 2. Converts it to a MARC field for storage in the bib record.
1608 # The previous behavior was:
1609 # 1. Get the bib record.
1610 # 2. Return the MARC tag corresponding to the item record.
1612 # The difference is that one treats the items row as authoritative,
1613 # while the other treats the MARC representation as authoritative
1614 # under certain circumstances.
1616 my $itemrecord = GetItem($itemnumber);
1618 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1619 # Also, don't emit a subfield if the underlying field is blank.
1622 return Item2Marc($itemrecord,$biblionumber);
1626 my ($itemrecord,$biblionumber)=@_;
1629 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1630 } keys %{ $itemrecord }
1632 my $itemmarc = TransformKohaToMarc($mungeditem);
1633 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1635 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1636 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1637 foreach my $field ($itemmarc->field($itemtag)){
1638 $field->add_subfields(@$unlinked_item_subfields);
1644 =head1 PRIVATE FUNCTIONS AND VARIABLES
1646 The following functions are not meant to be called
1647 directly, but are documented in order to explain
1648 the inner workings of C<C4::Items>.
1652 =head2 %derived_columns
1654 This hash keeps track of item columns that
1655 are strictly derived from other columns in
1656 the item record and are not meant to be set
1659 Each key in the hash should be the name of a
1660 column (as named by TransformMarcToKoha). Each
1661 value should be hashref whose keys are the
1662 columns on which the derived column depends. The
1663 hashref should also contain a 'BUILDER' key
1664 that is a reference to a sub that calculates
1669 my %derived_columns = (
1670 'items.cn_sort' => {
1671 'itemcallnumber' => 1,
1672 'items.cn_source' => 1,
1673 'BUILDER' => \&_calc_items_cn_sort,
1677 =head2 _set_derived_columns_for_add
1679 _set_derived_column_for_add($item);
1681 Given an item hash representing a new item to be added,
1682 calculate any derived columns. Currently the only
1683 such column is C<items.cn_sort>.
1687 sub _set_derived_columns_for_add {
1690 foreach my $column (keys %derived_columns) {
1691 my $builder = $derived_columns{$column}->{'BUILDER'};
1692 my $source_values = {};
1693 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1694 next if $source_column eq 'BUILDER';
1695 $source_values->{$source_column} = $item->{$source_column};
1697 $builder->($item, $source_values);
1701 =head2 _set_derived_columns_for_mod
1703 _set_derived_column_for_mod($item);
1705 Given an item hash representing a new item to be modified.
1706 calculate any derived columns. Currently the only
1707 such column is C<items.cn_sort>.
1709 This routine differs from C<_set_derived_columns_for_add>
1710 in that it needs to handle partial item records. In other
1711 words, the caller of C<ModItem> may have supplied only one
1712 or two columns to be changed, so this function needs to
1713 determine whether any of the columns to be changed affect
1714 any of the derived columns. Also, if a derived column
1715 depends on more than one column, but the caller is not
1716 changing all of then, this routine retrieves the unchanged
1717 values from the database in order to ensure a correct
1722 sub _set_derived_columns_for_mod {
1725 foreach my $column (keys %derived_columns) {
1726 my $builder = $derived_columns{$column}->{'BUILDER'};
1727 my $source_values = {};
1728 my %missing_sources = ();
1729 my $must_recalc = 0;
1730 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1731 next if $source_column eq 'BUILDER';
1732 if (exists $item->{$source_column}) {
1734 $source_values->{$source_column} = $item->{$source_column};
1736 $missing_sources{$source_column} = 1;
1740 foreach my $source_column (keys %missing_sources) {
1741 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1743 $builder->($item, $source_values);
1748 =head2 _do_column_fixes_for_mod
1750 _do_column_fixes_for_mod($item);
1752 Given an item hashref containing one or more
1753 columns to modify, fix up certain values.
1754 Specifically, set to 0 any passed value
1755 of C<notforloan>, C<damaged>, C<itemlost>, or
1756 C<wthdrawn> that is either undefined or
1757 contains the empty string.
1761 sub _do_column_fixes_for_mod {
1764 if (exists $item->{'notforloan'} and
1765 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1766 $item->{'notforloan'} = 0;
1768 if (exists $item->{'damaged'} and
1769 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1770 $item->{'damaged'} = 0;
1772 if (exists $item->{'itemlost'} and
1773 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1774 $item->{'itemlost'} = 0;
1776 if (exists $item->{'wthdrawn'} and
1777 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1778 $item->{'wthdrawn'} = 0;
1780 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1781 $item->{'permanent_location'} = $item->{'location'};
1783 if (exists $item->{'timestamp'}) {
1784 delete $item->{'timestamp'};
1788 =head2 _get_single_item_column
1790 _get_single_item_column($column, $itemnumber);
1792 Retrieves the value of a single column from an C<items>
1793 row specified by C<$itemnumber>.
1797 sub _get_single_item_column {
1799 my $itemnumber = shift;
1801 my $dbh = C4::Context->dbh;
1802 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1803 $sth->execute($itemnumber);
1804 my ($value) = $sth->fetchrow();
1808 =head2 _calc_items_cn_sort
1810 _calc_items_cn_sort($item, $source_values);
1812 Helper routine to calculate C<items.cn_sort>.
1816 sub _calc_items_cn_sort {
1818 my $source_values = shift;
1820 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1823 =head2 _set_defaults_for_add
1825 _set_defaults_for_add($item_hash);
1827 Given an item hash representing an item to be added, set
1828 correct default values for columns whose default value
1829 is not handled by the DBMS. This includes the following
1836 C<items.dateaccessioned>
1858 sub _set_defaults_for_add {
1860 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1861 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1864 =head2 _koha_new_item
1866 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1868 Perform the actual insert into the C<items> table.
1872 sub _koha_new_item {
1873 my ( $item, $barcode ) = @_;
1874 my $dbh=C4::Context->dbh;
1877 "INSERT INTO items SET
1879 biblioitemnumber = ?,
1881 dateaccessioned = ?,
1885 replacementprice = ?,
1886 replacementpricedate = NOW(),
1887 datelastborrowed = ?,
1888 datelastseen = NOW(),
1911 more_subfields_xml = ?,
1914 my $sth = $dbh->prepare($query);
1916 $item->{'biblionumber'},
1917 $item->{'biblioitemnumber'},
1919 $item->{'dateaccessioned'},
1920 $item->{'booksellerid'},
1921 $item->{'homebranch'},
1923 $item->{'replacementprice'},
1924 $item->{datelastborrowed},
1926 $item->{'notforloan'},
1928 $item->{'itemlost'},
1929 $item->{'wthdrawn'},
1930 $item->{'itemcallnumber'},
1931 $item->{'restricted'},
1932 $item->{'itemnotes'},
1933 $item->{'holdingbranch'},
1935 $item->{'location'},
1938 $item->{'renewals'},
1939 $item->{'reserves'},
1940 $item->{'items.cn_source'},
1941 $item->{'items.cn_sort'},
1944 $item->{'materials'},
1946 $item->{'enumchron'},
1947 $item->{'more_subfields_xml'},
1948 $item->{'copynumber'},
1950 my $itemnumber = $dbh->{'mysql_insertid'};
1951 if ( defined $sth->errstr ) {
1952 $error.="ERROR in _koha_new_item $query".$sth->errstr;
1954 return ( $itemnumber, $error );
1957 =head2 MoveItemFromBiblio
1959 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
1961 Moves an item from a biblio to another
1963 Returns undef if the move failed or the biblionumber of the destination record otherwise
1967 sub MoveItemFromBiblio {
1968 my ($itemnumber, $frombiblio, $tobiblio) = @_;
1969 my $dbh = C4::Context->dbh;
1970 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
1971 $sth->execute( $tobiblio );
1972 my ( $tobiblioitem ) = $sth->fetchrow();
1973 $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
1974 my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
1978 my $frameworkcode = GetFrameworkCode($frombiblio);
1980 # Getting marc field for itemnumber
1981 my ($itemtag, $itemsubfield) = GetMarcFromKohaField('items.itemnumber', $frameworkcode);
1983 # Getting the record we want to move the item from
1984 my $record = GetMarcBiblio($frombiblio);
1986 # The item we want to move
1990 foreach my $fielditem ($record->field($itemtag)){
1991 # If it is the item we want to move
1992 if ($fielditem->subfield($itemsubfield) == $itemnumber) {
1995 # Then delete it from the record
1996 $record->delete_field($fielditem)
2000 # If we found an item (should always true, except in case of database-marcxml inconsistency)
2003 # Checking if the item we want to move is in an order
2004 my $order = GetOrderFromItemnumber($itemnumber);
2006 # Replacing the biblionumber within the order if necessary
2007 $order->{'biblionumber'} = $tobiblio;
2011 # Saving the modification
2012 ModBiblioMarc($record, $frombiblio, $frameworkcode);
2014 # Getting the record we want to move the item to
2015 $record = GetMarcBiblio($tobiblio);
2017 # Inserting the previously saved item
2018 $record->insert_fields_ordered($item);
2020 # Saving the modification
2021 ModBiblioMarc($record, $tobiblio, $frameworkcode);
2033 DelItemCheck($dbh, $biblionumber, $itemnumber);
2035 Exported function (core API) for deleting an item record in Koha if there no current issue.
2040 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2043 # check that there is no issue on this item before deletion.
2044 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2045 $sth->execute($itemnumber);
2047 my $item = GetItem($itemnumber);
2048 my $onloan = $sth->fetchrow;
2050 $error = "book_on_loan";
2052 elsif (C4::Context->preference("IndependantBranches") and (C4::Context->userenv->{branch} ne $item->{C4::Context->preference("HomeOrHoldingBranch")||'homebranch'})){
2053 $error = "not_same_branch";
2057 $error = "book_on_loan"
2060 # check it doesnt have a waiting reserve
2061 $sth=$dbh->prepare("SELECT * FROM reserves WHERE (found = 'W' or found = 'T') AND itemnumber = ?");
2062 $sth->execute($itemnumber);
2063 my $reserve=$sth->fetchrow;
2065 $error = "book_reserved";
2068 DelItem($dbh, $biblionumber, $itemnumber);
2076 =head2 _koha_modify_item
2078 my ($itemnumber,$error) =_koha_modify_item( $item );
2080 Perform the actual update of the C<items> row. Note that this
2081 routine accepts a hashref specifying the columns to update.
2085 sub _koha_modify_item {
2087 my $dbh=C4::Context->dbh;
2090 my $query = "UPDATE items SET ";
2092 for my $key ( keys %$item ) {
2094 push @bind, $item->{$key};
2097 $query .= " WHERE itemnumber=?";
2098 push @bind, $item->{'itemnumber'};
2099 my $sth = C4::Context->dbh->prepare($query);
2100 $sth->execute(@bind);
2101 if ( C4::Context->dbh->errstr ) {
2102 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2105 return ($item->{'itemnumber'},$error);
2108 =head2 _koha_delete_item
2110 _koha_delete_item( $dbh, $itemnum );
2112 Internal function to delete an item record from the koha tables
2116 sub _koha_delete_item {
2117 my ( $dbh, $itemnum ) = @_;
2119 # save the deleted item to deleteditems table
2120 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2121 $sth->execute($itemnum);
2122 my $data = $sth->fetchrow_hashref();
2123 my $query = "INSERT INTO deleteditems SET ";
2125 foreach my $key ( keys %$data ) {
2126 $query .= "$key = ?,";
2127 push( @bind, $data->{$key} );
2130 $sth = $dbh->prepare($query);
2131 $sth->execute(@bind);
2133 # delete from items table
2134 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2135 $sth->execute($itemnum);
2139 =head2 _marc_from_item_hash
2141 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2143 Given an item hash representing a complete item record,
2144 create a C<MARC::Record> object containing an embedded
2145 tag representing that item.
2147 The third, optional parameter C<$unlinked_item_subfields> is
2148 an arrayref of subfields (not mapped to C<items> fields per the
2149 framework) to be added to the MARC representation
2154 sub _marc_from_item_hash {
2156 my $frameworkcode = shift;
2157 my $unlinked_item_subfields;
2159 $unlinked_item_subfields = shift;
2162 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2163 # Also, don't emit a subfield if the underlying field is blank.
2164 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2165 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2166 : () } keys %{ $item } };
2168 my $item_marc = MARC::Record->new();
2169 foreach my $item_field ( keys %{$mungeditem} ) {
2170 my ( $tag, $subfield ) = GetMarcFromKohaField( $item_field, $frameworkcode );
2171 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2172 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2173 foreach my $value (@values){
2174 if ( my $field = $item_marc->field($tag) ) {
2175 $field->add_subfields( $subfield => $value );
2177 my $add_subfields = [];
2178 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2179 $add_subfields = $unlinked_item_subfields;
2181 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields );
2189 =head2 _add_item_field_to_biblio
2191 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2193 Adds the fields from a MARC record containing the
2194 representation of a Koha item record to the MARC
2195 biblio record. The input C<$item_marc> record
2196 is expect to contain just one field, the embedded
2197 item information field.
2201 sub _add_item_field_to_biblio {
2202 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2204 my $biblio_marc = GetMarcBiblio($biblionumber);
2205 foreach my $field ($item_marc->fields()) {
2206 $biblio_marc->append_fields($field);
2209 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2212 =head2 _replace_item_field_in_biblio
2214 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2216 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2217 representation of the item, examine the biblio MARC
2218 for the corresponding tag for that item and
2219 replace it with the tag from C<$item_marc>.
2223 sub _replace_item_field_in_biblio {
2224 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2225 my $dbh = C4::Context->dbh;
2227 # get complete MARC record & replace the item field by the new one
2228 my $completeRecord = GetMarcBiblio($biblionumber);
2229 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2230 my $itemField = $ItemRecord->field($itemtag);
2231 my @items = $completeRecord->field($itemtag);
2234 if ($_->subfield($itemsubfield) eq $itemnumber) {
2235 $_->replace_with($itemField);
2241 # If we haven't found the matching field,
2242 # just add it. However, this means that
2243 # there is likely a bug.
2244 $completeRecord->append_fields($itemField);
2248 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2251 =head2 _repack_item_errors
2253 Add an error message hash generated by C<CheckItemPreSave>
2254 to a list of errors.
2258 sub _repack_item_errors {
2259 my $item_sequence_num = shift;
2260 my $item_ref = shift;
2261 my $error_ref = shift;
2263 my @repacked_errors = ();
2265 foreach my $error_code (sort keys %{ $error_ref }) {
2266 my $repacked_error = {};
2267 $repacked_error->{'item_sequence'} = $item_sequence_num;
2268 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2269 $repacked_error->{'error_code'} = $error_code;
2270 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2271 push @repacked_errors, $repacked_error;
2274 return @repacked_errors;
2277 =head2 _get_unlinked_item_subfields
2279 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2283 sub _get_unlinked_item_subfields {
2284 my $original_item_marc = shift;
2285 my $frameworkcode = shift;
2287 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2289 # assume that this record has only one field, and that that
2290 # field contains only the item information
2292 my @fields = $original_item_marc->fields();
2293 if ($#fields > -1) {
2294 my $field = $fields[0];
2295 my $tag = $field->tag();
2296 foreach my $subfield ($field->subfields()) {
2297 if (defined $subfield->[1] and
2298 $subfield->[1] ne '' and
2299 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2300 push @$subfields, $subfield->[0] => $subfield->[1];
2307 =head2 _get_unlinked_subfields_xml
2309 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2313 sub _get_unlinked_subfields_xml {
2314 my $unlinked_item_subfields = shift;
2317 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2318 my $marc = MARC::Record->new();
2319 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2320 # used in the framework
2321 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2322 $marc->encoding("UTF-8");
2323 $xml = $marc->as_xml("USMARC");
2329 =head2 _parse_unlinked_item_subfields_from_xml
2331 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2335 sub _parse_unlinked_item_subfields_from_xml {
2338 return unless defined $xml and $xml ne "";
2339 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2340 my $unlinked_subfields = [];
2341 my @fields = $marc->fields();
2342 if ($#fields > -1) {
2343 foreach my $subfield ($fields[0]->subfields()) {
2344 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2347 return $unlinked_subfields;