3 # Copyright 2007 LibLime, Inc.
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
25 use C4::Dates qw/format_date format_date_in_iso/;
33 use vars qw($VERSION @ISA @EXPORT);
39 @ISA = qw( Exporter );
62 GetItemsByBiblioitemnumber
65 GetItemnumberFromBarcode
71 C4::Items - item management functions
75 This module contains an API for manipulating item
76 records in Koha, and is used by cataloguing, circulation,
77 acquisitions, and serials management.
79 A Koha item record is stored in two places: the
80 items table and embedded in a MARC tag in the XML
81 version of the associated bib record in C<biblioitems.marcxml>.
82 This is done to allow the item information to be readily
83 indexed (e.g., by Zebra), but means that each item
84 modification transaction must keep the items table
85 and the MARC XML in sync at all times.
87 Consequently, all code that creates, modifies, or deletes
88 item records B<must> use an appropriate function from
89 C<C4::Items>. If no existing function is suitable, it is
90 better to add one to C<C4::Items> than to use add
91 one-off SQL statements to add or modify items.
93 The items table will be considered authoritative. In other
94 words, if there is ever a discrepancy between the items
95 table and the MARC XML, the items table should be considered
98 =head1 HISTORICAL NOTE
100 Most of the functions in C<C4::Items> were originally in
101 the C<C4::Biblio> module.
103 =head1 CORE EXPORTED FUNCTIONS
105 The following functions are meant for use by users
114 $item = GetItem($itemnumber,$barcode,$serial);
118 Return item information, for a given itemnumber or barcode.
119 The return value is a hashref mapping item column
120 names to values. If C<$serial> is true, include serial publication data.
125 my ($itemnumber,$barcode, $serial) = @_;
126 my $dbh = C4::Context->dbh;
129 my $sth = $dbh->prepare("
131 WHERE itemnumber = ?");
132 $sth->execute($itemnumber);
133 $data = $sth->fetchrow_hashref;
135 my $sth = $dbh->prepare("
139 $sth->execute($barcode);
140 $data = $sth->fetchrow_hashref;
143 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
144 $ssth->execute($data->{'itemnumber'}) ;
145 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
146 warn $data->{'serialseq'} , $data->{'publisheddate'};
148 #if we don't have an items.itype, use biblioitems.itemtype.
149 if( ! $data->{'itype'} ) {
150 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
151 $sth->execute($data->{'biblionumber'});
152 ($data->{'itype'}) = $sth->fetchrow_array;
157 =head2 AddItemFromMarc
161 my ($biblionumber, $biblioitemnumber, $itemnumber)
162 = AddItemFromMarc($source_item_marc, $biblionumber);
166 Given a MARC::Record object containing an embedded item
167 record and a biblionumber, create a new item record.
171 sub AddItemFromMarc {
172 my ( $source_item_marc, $biblionumber ) = @_;
173 my $dbh = C4::Context->dbh;
175 # parse item hash from MARC
176 my $frameworkcode = GetFrameworkCode( $biblionumber );
177 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
179 my $localitemmarc=MARC::Record->new;
180 $localitemmarc->append_fields($source_item_marc->field($itemtag));
181 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
182 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
183 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
190 my ($biblionumber, $biblioitemnumber, $itemnumber)
191 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
195 Given a hash containing item column names as keys,
196 create a new Koha item record.
198 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
199 do not need to be supplied for general use; they exist
200 simply to allow them to be picked up from AddItemFromMarc.
202 The final optional parameter, C<$unlinked_item_subfields>, contains
203 an arrayref containing subfields present in the original MARC
204 representation of the item (e.g., from the item editor) that are
205 not mapped to C<items> columns directly but should instead
206 be stored in C<items.more_subfields_xml> and included in
207 the biblio items tag for display and indexing.
213 my $biblionumber = shift;
215 my $dbh = @_ ? shift : C4::Context->dbh;
216 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
217 my $unlinked_item_subfields;
219 $unlinked_item_subfields = shift
222 # needs old biblionumber and biblioitemnumber
223 $item->{'biblionumber'} = $biblionumber;
224 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
225 $sth->execute( $item->{'biblionumber'} );
226 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
228 _set_defaults_for_add($item);
229 _set_derived_columns_for_add($item);
230 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
231 # FIXME - checks here
232 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
233 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
234 $itype_sth->execute( $item->{'biblionumber'} );
235 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
238 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
239 $item->{'itemnumber'} = $itemnumber;
241 # create MARC tag representing item and add to bib
242 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
243 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
245 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
247 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
250 =head2 AddItemBatchFromMarc
254 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, $biblionumber, $biblioitemnumber, $frameworkcode);
258 Efficiently create item records from a MARC biblio record with
259 embedded item fields. This routine is suitable for batch jobs.
261 This API assumes that the bib record has already been
262 saved to the C<biblio> and C<biblioitems> tables. It does
263 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
264 are populated, but it will do so via a call to ModBibiloMarc.
266 The goal of this API is to have a similar effect to using AddBiblio
267 and AddItems in succession, but without inefficient repeated
268 parsing of the MARC XML bib record.
270 This function returns an arrayref of new itemsnumbers and an arrayref of item
271 errors encountered during the processing. Each entry in the errors
272 list is a hashref containing the following keys:
278 Sequence number of original item tag in the MARC record.
282 Item barcode, provide to assist in the construction of
283 useful error messages.
285 =item error_condition
287 Code representing the error condition. Can be 'duplicate_barcode',
288 'invalid_homebranch', or 'invalid_holdingbranch'.
290 =item error_information
292 Additional information appropriate to the error condition.
298 sub AddItemBatchFromMarc {
299 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
301 my @itemnumbers = ();
303 my $dbh = C4::Context->dbh;
305 # loop through the item tags and start creating items
306 my @bad_item_fields = ();
307 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
308 my $item_sequence_num = 0;
309 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
310 $item_sequence_num++;
311 # we take the item field and stick it into a new
312 # MARC record -- this is required so far because (FIXME)
313 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
314 # and there is no TransformMarcFieldToKoha
315 my $temp_item_marc = MARC::Record->new();
316 $temp_item_marc->append_fields($item_field);
318 # add biblionumber and biblioitemnumber
319 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
320 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
321 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
322 $item->{'biblionumber'} = $biblionumber;
323 $item->{'biblioitemnumber'} = $biblioitemnumber;
325 # check for duplicate barcode
326 my %item_errors = CheckItemPreSave($item);
328 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
329 push @bad_item_fields, $item_field;
333 _set_defaults_for_add($item);
334 _set_derived_columns_for_add($item);
335 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
336 warn $error if $error;
337 push @itemnumbers, $itemnumber; # FIXME not checking error
338 $item->{'itemnumber'} = $itemnumber;
340 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
342 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
343 $item_field->replace_with($new_item_marc->field($itemtag));
346 # remove any MARC item fields for rejected items
347 foreach my $item_field (@bad_item_fields) {
348 $record->delete_field($item_field);
351 # update the MARC biblio
352 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
354 return (\@itemnumbers, \@errors);
357 =head2 ModItemFromMarc
361 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
365 This function updates an item record based on a supplied
366 C<MARC::Record> object containing an embedded item field.
367 This API is meant for the use of C<additem.pl>; for
368 other purposes, C<ModItem> should be used.
370 This function uses the hash %default_values_for_mod_from_marc,
371 which contains default values for item fields to
372 apply when modifying an item. This is needed beccause
373 if an item field's value is cleared, TransformMarcToKoha
374 does not include the column in the
375 hash that's passed to ModItem, which without
376 use of this hash makes it impossible to clear
377 an item field's value. See bug 2466.
379 Note that only columns that can be directly
380 changed from the cataloging and serials
381 item editors are included in this hash.
385 my %default_values_for_mod_from_marc = (
387 booksellerid => undef,
389 'items.cn_source' => undef,
392 dateaccessioned => undef,
394 holdingbranch => undef,
396 itemcallnumber => undef,
405 replacementprice => undef,
406 replacementpricedate => undef,
413 sub ModItemFromMarc {
414 my $item_marc = shift;
415 my $biblionumber = shift;
416 my $itemnumber = shift;
418 my $dbh = C4::Context->dbh;
419 my $frameworkcode = GetFrameworkCode( $biblionumber );
420 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
422 my $localitemmarc=MARC::Record->new;
423 $localitemmarc->append_fields($item_marc->field($itemtag));
424 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items');
425 foreach my $item_field (keys %default_values_for_mod_from_marc) {
426 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
428 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
430 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
437 ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]);
441 Change one or more columns in an item record and update
442 the MARC representation of the item.
444 The first argument is a hashref mapping from item column
445 names to the new values. The second and third arguments
446 are the biblionumber and itemnumber, respectively.
448 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
449 an arrayref containing subfields present in the original MARC
450 representation of the item (e.g., from the item editor) that are
451 not mapped to C<items> columns directly but should instead
452 be stored in C<items.more_subfields_xml> and included in
453 the biblio items tag for display and indexing.
455 If one of the changed columns is used to calculate
456 the derived value of a column such as C<items.cn_sort>,
457 this routine will perform the necessary calculation
464 my $biblionumber = shift;
465 my $itemnumber = shift;
467 # if $biblionumber is undefined, get it from the current item
468 unless (defined $biblionumber) {
469 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
472 my $dbh = @_ ? shift : C4::Context->dbh;
473 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
475 my $unlinked_item_subfields;
477 $unlinked_item_subfields = shift;
478 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
481 $item->{'itemnumber'} = $itemnumber or return undef;
482 _set_derived_columns_for_mod($item);
483 _do_column_fixes_for_mod($item);
486 # attempt to change itemnumber
487 # attempt to change biblionumber (if we want
488 # an API to relink an item to a different bib,
489 # it should be a separate function)
492 _koha_modify_item($item);
494 # update biblio MARC XML
495 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
497 unless (defined $unlinked_item_subfields) {
498 $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
500 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields)
501 or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
503 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
504 ($new_item_marc eq '0') and die "$new_item_marc is '0', not hashref"; # logaction line would crash anyway
505 logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
508 =head2 ModItemTransfer
512 ModItemTransfer($itenumber, $frombranch, $tobranch);
516 Marks an item as being transferred from one branch
521 sub ModItemTransfer {
522 my ( $itemnumber, $frombranch, $tobranch ) = @_;
524 my $dbh = C4::Context->dbh;
526 #new entry in branchtransfers....
527 my $sth = $dbh->prepare(
528 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
529 VALUES (?, ?, NOW(), ?)");
530 $sth->execute($itemnumber, $frombranch, $tobranch);
532 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
533 ModDateLastSeen($itemnumber);
537 =head2 ModDateLastSeen
541 ModDateLastSeen($itemnum);
545 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
546 C<$itemnum> is the item number
550 sub ModDateLastSeen {
551 my ($itemnumber) = @_;
553 my $today = C4::Dates->new();
554 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
561 DelItem($biblionumber, $itemnumber);
565 Exported function (core API) for deleting an item record in Koha.
570 my ( $dbh, $biblionumber, $itemnumber ) = @_;
572 # FIXME check the item has no current issues
574 _koha_delete_item( $dbh, $itemnumber );
576 # get the MARC record
577 my $record = GetMarcBiblio($biblionumber);
578 my $frameworkcode = GetFrameworkCode($biblionumber);
581 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
582 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
584 #search item field code
585 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
586 my @fields = $record->field($itemtag);
588 # delete the item specified
589 foreach my $field (@fields) {
590 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
591 $record->delete_field($field);
594 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
595 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
602 DelItemCheck($dbh, $biblionumber, $itemnumber);
606 Exported function (core API) for deleting an item record in Koha if there no current issue.
611 my ( $dbh, $biblionumber, $itemnumber ) = @_;
614 # check that there is no issue on this item before deletion.
615 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
616 $sth->execute($itemnumber);
618 my $onloan=$sth->fetchrow;
622 $error = "book_on_loan"
624 # check it doesnt have a waiting reserve
625 $sth=$dbh->prepare("SELECT * FROM reserves WHERE found = 'W' AND itemnumber = ?");
626 $sth->execute($itemnumber);
627 my $reserve=$sth->fetchrow;
630 $error = "book_reserved";
632 DelItem($dbh, $biblionumber, $itemnumber);
639 =head2 CheckItemPreSave
643 my $item_ref = TransformMarcToKoha($marc, 'items');
645 my %errors = CheckItemPreSave($item_ref);
646 if (exists $errors{'duplicate_barcode'}) {
647 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
648 } elsif (exists $errors{'invalid_homebranch'}) {
649 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
650 } elsif (exists $errors{'invalid_holdingbranch'}) {
651 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
658 Given a hashref containing item fields, determine if it can be
659 inserted or updated in the database. Specifically, checks for
660 database integrity issues, and returns a hash containing any
661 of the following keys, if applicable.
665 =item duplicate_barcode
667 Barcode, if it duplicates one already found in the database.
669 =item invalid_homebranch
671 Home branch, if not defined in branches table.
673 =item invalid_holdingbranch
675 Holding branch, if not defined in branches table.
679 This function does NOT implement any policy-related checks,
680 e.g., whether current operator is allowed to save an
681 item that has a given branch code.
685 sub CheckItemPreSave {
686 my $item_ref = shift;
690 # check for duplicate barcode
691 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
692 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
693 if ($existing_itemnumber) {
694 if (!exists $item_ref->{'itemnumber'} # new item
695 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
696 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
701 # check for valid home branch
702 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
703 my $branch_name = GetBranchName($item_ref->{'homebranch'});
704 unless (defined $branch_name) {
705 # relies on fact that branches.branchname is a non-NULL column,
706 # so GetBranchName returns undef only if branch does not exist
707 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
711 # check for valid holding branch
712 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
713 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
714 unless (defined $branch_name) {
715 # relies on fact that branches.branchname is a non-NULL column,
716 # so GetBranchName returns undef only if branch does not exist
717 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
725 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
727 The following functions provide various ways of
728 getting an item record, a set of item records, or
729 lists of authorized values for certain item fields.
731 Some of the functions in this group are candidates
732 for refactoring -- for example, some of the code
733 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
734 has copy-and-paste work.
742 $itemstatushash = GetItemStatus($fwkcode);
746 Returns a list of valid values for the
747 C<items.notforloan> field.
749 NOTE: does B<not> return an individual item's
752 Can be MARC dependant.
754 But basically could be can be loan or not
755 Create a status selector with the following code
757 =head3 in PERL SCRIPT
761 my $itemstatushash = getitemstatus;
763 foreach my $thisstatus (keys %$itemstatushash) {
764 my %row =(value => $thisstatus,
765 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
767 push @itemstatusloop, \%row;
769 $template->param(statusloop=>\@itemstatusloop);
777 <select name="statusloop">
778 <option value="">Default</option>
779 <!-- TMPL_LOOP name="statusloop" -->
780 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
790 # returns a reference to a hash of references to status...
793 my $dbh = C4::Context->dbh;
795 $fwk = '' unless ($fwk);
796 my ( $tag, $subfield ) =
797 GetMarcFromKohaField( "items.notforloan", $fwk );
798 if ( $tag and $subfield ) {
801 "SELECT authorised_value
802 FROM marc_subfield_structure
808 $sth->execute( $tag, $subfield, $fwk );
809 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
812 "SELECT authorised_value,lib
813 FROM authorised_values
818 $authvalsth->execute($authorisedvaluecat);
819 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
820 $itemstatus{$authorisedvalue} = $lib;
836 $itemstatus{"1"} = "Not For Loan";
840 =head2 GetItemLocation
844 $itemlochash = GetItemLocation($fwk);
848 Returns a list of valid values for the
849 C<items.location> field.
851 NOTE: does B<not> return an individual item's
854 where fwk stands for an optional framework code.
855 Create a location selector with the following code
857 =head3 in PERL SCRIPT
861 my $itemlochash = getitemlocation;
863 foreach my $thisloc (keys %$itemlochash) {
864 my $selected = 1 if $thisbranch eq $branch;
865 my %row =(locval => $thisloc,
866 selected => $selected,
867 locname => $itemlochash->{$thisloc},
869 push @itemlocloop, \%row;
871 $template->param(itemlocationloop => \@itemlocloop);
879 <select name="location">
880 <option value="">Default</option>
881 <!-- TMPL_LOOP name="itemlocationloop" -->
882 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
890 sub GetItemLocation {
892 # returns a reference to a hash of references to location...
895 my $dbh = C4::Context->dbh;
897 $fwk = '' unless ($fwk);
898 my ( $tag, $subfield ) =
899 GetMarcFromKohaField( "items.location", $fwk );
900 if ( $tag and $subfield ) {
903 "SELECT authorised_value
904 FROM marc_subfield_structure
909 $sth->execute( $tag, $subfield, $fwk );
910 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
913 "SELECT authorised_value,lib
914 FROM authorised_values
918 $authvalsth->execute($authorisedvaluecat);
919 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
920 $itemlocation{$authorisedvalue} = $lib;
923 return \%itemlocation;
936 $itemlocation{"1"} = "Not For Loan";
937 return \%itemlocation;
944 $items = GetLostItems( $where, $orderby );
948 This function gets a list of lost items.
954 C<$where> is a hashref. it containts a field of the items table as key
955 and the value to match as value. For example:
957 { barcode => 'abc123',
958 homebranch => 'CPL', }
960 C<$orderby> is a field of the items table by which the resultset
965 C<$items> is a reference to an array full of hashrefs with columns
966 from the "items" table as keys.
968 =item usage in the perl script:
970 my $where = { barcode => '0001548' };
971 my $items = GetLostItems( $where, "homebranch" );
972 $template->param( itemsloop => $items );
979 # Getting input args.
982 my $dbh = C4::Context->dbh;
987 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
988 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
989 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
991 authorised_values.category = 'LOST'
992 AND itemlost IS NOT NULL
995 my @query_parameters;
996 foreach my $key (keys %$where) {
997 $query .= " AND $key LIKE ?";
998 push @query_parameters, "%$where->{$key}%";
1000 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
1002 if ( defined $orderby && grep($orderby, @ordervalues)) {
1003 $query .= ' ORDER BY '.$orderby;
1006 my $sth = $dbh->prepare($query);
1007 $sth->execute( @query_parameters );
1009 while ( my $row = $sth->fetchrow_hashref ){
1015 =head2 GetItemsForInventory
1019 $itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size);
1023 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1025 The sub returns a reference to a list of hashes, each containing
1026 itemnumber, author, title, barcode, item callnumber, and date last
1027 seen. It is ordered by callnumber then title.
1029 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
1030 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1031 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1035 sub GetItemsForInventory {
1036 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branch, $offset, $size ) = @_;
1037 my $dbh = C4::Context->dbh;
1038 my ( @bind_params, @where_strings );
1040 my $query = <<'END_SQL';
1041 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
1043 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1044 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1048 push @where_strings, 'itemcallnumber >= ?';
1049 push @bind_params, $minlocation;
1053 push @where_strings, 'itemcallnumber <= ?';
1054 push @bind_params, $maxlocation;
1057 if ($datelastseen) {
1058 $datelastseen = format_date_in_iso($datelastseen);
1059 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1060 push @bind_params, $datelastseen;
1064 push @where_strings, 'items.location = ?';
1065 push @bind_params, $location;
1069 push @where_strings, 'items.homebranch = ?';
1070 push @bind_params, $branch;
1074 push @where_strings, 'biblioitems.itemtype = ?';
1075 push @bind_params, $itemtype;
1077 if ( $ignoreissued) {
1078 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1079 push @where_strings, 'issues.date_due IS NULL';
1082 if ( $ignoreissued) {
1083 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1084 push @where_strings, 'issues.date_due IS NULL';
1087 if ( @where_strings ) {
1089 $query .= join ' AND ', @where_strings;
1091 $query .= ' ORDER BY itemcallnumber, title';
1092 my $sth = $dbh->prepare($query);
1093 $sth->execute( @bind_params );
1097 while ( my $row = $sth->fetchrow_hashref ) {
1098 $offset-- if ($offset);
1099 $row->{datelastseen}=format_date($row->{datelastseen});
1100 if ( ( !$offset ) && $size ) {
1101 push @results, $row;
1108 =head2 GetItemsCount
1111 $count = &GetItemsCount( $biblionumber);
1115 This function return count of item with $biblionumber
1120 my ( $biblionumber ) = @_;
1121 my $dbh = C4::Context->dbh;
1122 my $query = "SELECT count(*)
1124 WHERE biblionumber=?";
1125 my $sth = $dbh->prepare($query);
1126 $sth->execute($biblionumber);
1127 my $count = $sth->fetchrow;
1132 =head2 GetItemInfosOf
1136 GetItemInfosOf(@itemnumbers);
1142 sub GetItemInfosOf {
1143 my @itemnumbers = @_;
1148 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1150 return get_infos_of( $query, 'itemnumber' );
1153 =head2 GetItemsByBiblioitemnumber
1157 GetItemsByBiblioitemnumber($biblioitemnumber);
1161 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1162 Called by C<C4::XISBN>
1166 sub GetItemsByBiblioitemnumber {
1167 my ( $bibitem ) = @_;
1168 my $dbh = C4::Context->dbh;
1169 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1170 # Get all items attached to a biblioitem
1173 $sth->execute($bibitem) || die $sth->errstr;
1174 while ( my $data = $sth->fetchrow_hashref ) {
1175 # Foreach item, get circulation information
1176 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1177 WHERE itemnumber = ?
1178 AND issues.borrowernumber = borrowers.borrowernumber"
1180 $sth2->execute( $data->{'itemnumber'} );
1181 if ( my $data2 = $sth2->fetchrow_hashref ) {
1182 # if item is out, set the due date and who it is out too
1183 $data->{'date_due'} = $data2->{'date_due'};
1184 $data->{'cardnumber'} = $data2->{'cardnumber'};
1185 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1188 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1189 $data->{'date_due'} = '';
1192 # Find the last 3 people who borrowed this item.
1193 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1194 AND old_issues.borrowernumber = borrowers.borrowernumber
1195 ORDER BY returndate desc,timestamp desc LIMIT 3";
1196 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1197 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1199 while ( my $data2 = $sth2->fetchrow_hashref ) {
1200 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1201 $data->{"card$i2"} = $data2->{'cardnumber'};
1202 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1206 push(@results,$data);
1216 @results = GetItemsInfo($biblionumber, $type);
1220 Returns information about books with the given biblionumber.
1222 C<$type> may be either C<intra> or anything else. If it is not set to
1223 C<intra>, then the search will exclude lost, very overdue, and
1226 C<GetItemsInfo> returns a list of references-to-hash. Each element
1227 contains a number of keys. Most of them are table items from the
1228 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1229 Koha database. Other keys include:
1233 =item C<$data-E<gt>{branchname}>
1235 The name (not the code) of the branch to which the book belongs.
1237 =item C<$data-E<gt>{datelastseen}>
1239 This is simply C<items.datelastseen>, except that while the date is
1240 stored in YYYY-MM-DD format in the database, here it is converted to
1241 DD/MM/YYYY format. A NULL date is returned as C<//>.
1243 =item C<$data-E<gt>{datedue}>
1245 =item C<$data-E<gt>{class}>
1247 This is the concatenation of C<biblioitems.classification>, the book's
1248 Dewey code, and C<biblioitems.subclass>.
1250 =item C<$data-E<gt>{ocount}>
1252 I think this is the number of copies of the book available.
1254 =item C<$data-E<gt>{order}>
1256 If this is set, it is set to C<One Order>.
1263 my ( $biblionumber, $type ) = @_;
1264 my $dbh = C4::Context->dbh;
1265 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1271 biblioitems.itemtype,
1274 biblioitems.publicationyear,
1275 biblioitems.publishercode,
1276 biblioitems.volumedate,
1277 biblioitems.volumedesc,
1280 items.notforloan as itemnotforloan,
1281 itemtypes.description
1283 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1284 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1285 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1286 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1287 $query .= " WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
1288 my $sth = $dbh->prepare($query);
1289 $sth->execute($biblionumber);
1294 my $isth = $dbh->prepare(
1295 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1296 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1297 WHERE itemnumber = ?"
1299 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1300 while ( my $data = $sth->fetchrow_hashref ) {
1303 $isth->execute( $data->{'itemnumber'} );
1304 if ( my $idata = $isth->fetchrow_hashref ) {
1305 $data->{borrowernumber} = $idata->{borrowernumber};
1306 $data->{cardnumber} = $idata->{cardnumber};
1307 $data->{surname} = $idata->{surname};
1308 $data->{firstname} = $idata->{firstname};
1309 $datedue = $idata->{'date_due'};
1310 if (C4::Context->preference("IndependantBranches")){
1311 my $userenv = C4::Context->userenv;
1312 if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
1313 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1317 if ( $data->{'serial'}) {
1318 $ssth->execute($data->{'itemnumber'}) ;
1319 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1322 if ( $datedue eq '' ) {
1323 my ( $restype, $reserves ) =
1324 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1326 $count_reserves = $restype;
1331 #get branch information.....
1332 my $bsth = $dbh->prepare(
1333 "SELECT * FROM branches WHERE branchcode = ?
1336 $bsth->execute( $data->{'holdingbranch'} );
1337 if ( my $bdata = $bsth->fetchrow_hashref ) {
1338 $data->{'branchname'} = $bdata->{'branchname'};
1340 $data->{'datedue'} = $datedue;
1341 $data->{'count_reserves'} = $count_reserves;
1343 # get notforloan complete status if applicable
1344 my $sthnflstatus = $dbh->prepare(
1345 'SELECT authorised_value
1346 FROM marc_subfield_structure
1347 WHERE kohafield="items.notforloan"
1351 $sthnflstatus->execute;
1352 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1353 if ($authorised_valuecode) {
1354 $sthnflstatus = $dbh->prepare(
1355 "SELECT lib FROM authorised_values
1357 AND authorised_value=?"
1359 $sthnflstatus->execute( $authorised_valuecode,
1360 $data->{itemnotforloan} );
1361 my ($lib) = $sthnflstatus->fetchrow;
1362 $data->{notforloanvalue} = $lib;
1364 $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes'));
1366 # my stack procedures
1367 my $stackstatus = $dbh->prepare(
1368 'SELECT authorised_value
1369 FROM marc_subfield_structure
1370 WHERE kohafield="items.stack"
1373 $stackstatus->execute;
1375 ($authorised_valuecode) = $stackstatus->fetchrow;
1376 if ($authorised_valuecode) {
1377 $stackstatus = $dbh->prepare(
1379 FROM authorised_values
1381 AND authorised_value=?
1384 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1385 my ($lib) = $stackstatus->fetchrow;
1386 $data->{stack} = $lib;
1388 # Find the last 3 people who borrowed this item.
1389 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1390 WHERE itemnumber = ?
1391 AND old_issues.borrowernumber = borrowers.borrowernumber
1392 ORDER BY returndate DESC
1394 $sth2->execute($data->{'itemnumber'});
1396 while (my $data2 = $sth2->fetchrow_hashref()) {
1397 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1398 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1399 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1403 $results[$i] = $data;
1407 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1413 =head2 get_itemnumbers_of
1417 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1421 Given a list of biblionumbers, return the list of corresponding itemnumbers
1422 for each biblionumber.
1424 Return a reference on a hash where keys are biblionumbers and values are
1425 references on array of itemnumbers.
1429 sub get_itemnumbers_of {
1430 my @biblionumbers = @_;
1432 my $dbh = C4::Context->dbh;
1438 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1440 my $sth = $dbh->prepare($query);
1441 $sth->execute(@biblionumbers);
1445 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1446 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1449 return \%itemnumbers_of;
1452 =head2 GetItemnumberFromBarcode
1456 $result = GetItemnumberFromBarcode($barcode);
1462 sub GetItemnumberFromBarcode {
1464 my $dbh = C4::Context->dbh;
1467 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1468 $rq->execute($barcode);
1469 my ($result) = $rq->fetchrow;
1473 =head3 get_item_authorised_values
1475 find the types and values for all authorised values assigned to this item.
1480 returns: a hashref malling the authorised value to the value set for this itemnumber
1482 $authorised_values = {
1488 'RESTRICTED' => undef,
1491 'branches' => 'CPL',
1492 'cn_source' => undef,
1493 'itemtypes' => 'SER',
1496 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1500 sub get_item_authorised_values {
1501 my $itemnumber = shift;
1503 # assume that these entries in the authorised_value table are item level.
1504 my $query = q(SELECT distinct authorised_value, kohafield
1505 FROM marc_subfield_structure
1506 WHERE kohafield like 'item%'
1507 AND authorised_value != '' );
1509 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1510 my $iteminfo = GetItem( $itemnumber );
1511 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1513 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1514 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1515 $field =~ s/^items\.//;
1516 if ( exists $iteminfo->{ $field } ) {
1517 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1520 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1524 =head3 get_authorised_value_images
1526 find a list of icons that are appropriate for display based on the
1527 authorised values for a biblio.
1529 parameters: listref of authorised values, such as comes from
1530 get_item_authorised_values or
1531 from C4::Biblio::get_biblio_authorised_values
1533 returns: listref of hashrefs for each image. Each hashref looks like
1536 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1541 Notes: Currently, I put on the full path to the images on the staff
1542 side. This should either be configurable or not done at all. Since I
1543 have to deal with 'intranet' or 'opac' in
1544 get_biblio_authorised_values, perhaps I should be passing it in.
1548 sub get_authorised_value_images {
1549 my $authorised_values = shift;
1553 my $authorised_value_list = GetAuthorisedValues();
1554 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1555 foreach my $this_authorised_value ( @$authorised_value_list ) {
1556 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1557 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1558 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1559 if ( defined $this_authorised_value->{'imageurl'} ) {
1560 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1561 label => $this_authorised_value->{'lib'},
1562 category => $this_authorised_value->{'category'},
1563 value => $this_authorised_value->{'authorised_value'}, };
1568 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1573 =head1 LIMITED USE FUNCTIONS
1575 The following functions, while part of the public API,
1576 are not exported. This is generally because they are
1577 meant to be used by only one script for a specific
1578 purpose, and should not be used in any other context
1579 without careful thought.
1587 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1591 Returns MARC::Record of the item passed in parameter.
1592 This function is meant for use only in C<cataloguing/additem.pl>,
1593 where it is needed to support that script's MARC-like
1599 my ( $biblionumber, $itemnumber ) = @_;
1601 # GetMarcItem has been revised so that it does the following:
1602 # 1. Gets the item information from the items table.
1603 # 2. Converts it to a MARC field for storage in the bib record.
1605 # The previous behavior was:
1606 # 1. Get the bib record.
1607 # 2. Return the MARC tag corresponding to the item record.
1609 # The difference is that one treats the items row as authoritative,
1610 # while the other treats the MARC representation as authoritative
1611 # under certain circumstances.
1613 my $itemrecord = GetItem($itemnumber);
1615 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1616 # Also, don't emit a subfield if the underlying field is blank.
1619 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1620 } keys %{ $itemrecord }
1623 my $itemmarc = TransformKohaToMarc($mungeditem);
1625 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1626 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1627 my @fields = $itemmarc->fields();
1628 if ($#fields > -1) {
1629 $fields[0]->add_subfields(@$unlinked_item_subfields);
1637 =head1 PRIVATE FUNCTIONS AND VARIABLES
1639 The following functions are not meant to be called
1640 directly, but are documented in order to explain
1641 the inner workings of C<C4::Items>.
1645 =head2 %derived_columns
1647 This hash keeps track of item columns that
1648 are strictly derived from other columns in
1649 the item record and are not meant to be set
1652 Each key in the hash should be the name of a
1653 column (as named by TransformMarcToKoha). Each
1654 value should be hashref whose keys are the
1655 columns on which the derived column depends. The
1656 hashref should also contain a 'BUILDER' key
1657 that is a reference to a sub that calculates
1662 my %derived_columns = (
1663 'items.cn_sort' => {
1664 'itemcallnumber' => 1,
1665 'items.cn_source' => 1,
1666 'BUILDER' => \&_calc_items_cn_sort,
1670 =head2 _set_derived_columns_for_add
1674 _set_derived_column_for_add($item);
1678 Given an item hash representing a new item to be added,
1679 calculate any derived columns. Currently the only
1680 such column is C<items.cn_sort>.
1684 sub _set_derived_columns_for_add {
1687 foreach my $column (keys %derived_columns) {
1688 my $builder = $derived_columns{$column}->{'BUILDER'};
1689 my $source_values = {};
1690 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1691 next if $source_column eq 'BUILDER';
1692 $source_values->{$source_column} = $item->{$source_column};
1694 $builder->($item, $source_values);
1698 =head2 _set_derived_columns_for_mod
1702 _set_derived_column_for_mod($item);
1706 Given an item hash representing a new item to be modified.
1707 calculate any derived columns. Currently the only
1708 such column is C<items.cn_sort>.
1710 This routine differs from C<_set_derived_columns_for_add>
1711 in that it needs to handle partial item records. In other
1712 words, the caller of C<ModItem> may have supplied only one
1713 or two columns to be changed, so this function needs to
1714 determine whether any of the columns to be changed affect
1715 any of the derived columns. Also, if a derived column
1716 depends on more than one column, but the caller is not
1717 changing all of then, this routine retrieves the unchanged
1718 values from the database in order to ensure a correct
1723 sub _set_derived_columns_for_mod {
1726 foreach my $column (keys %derived_columns) {
1727 my $builder = $derived_columns{$column}->{'BUILDER'};
1728 my $source_values = {};
1729 my %missing_sources = ();
1730 my $must_recalc = 0;
1731 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1732 next if $source_column eq 'BUILDER';
1733 if (exists $item->{$source_column}) {
1735 $source_values->{$source_column} = $item->{$source_column};
1737 $missing_sources{$source_column} = 1;
1741 foreach my $source_column (keys %missing_sources) {
1742 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1744 $builder->($item, $source_values);
1749 =head2 _do_column_fixes_for_mod
1753 _do_column_fixes_for_mod($item);
1757 Given an item hashref containing one or more
1758 columns to modify, fix up certain values.
1759 Specifically, set to 0 any passed value
1760 of C<notforloan>, C<damaged>, C<itemlost>, or
1761 C<wthdrawn> that is either undefined or
1762 contains the empty string.
1766 sub _do_column_fixes_for_mod {
1769 if (exists $item->{'notforloan'} and
1770 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1771 $item->{'notforloan'} = 0;
1773 if (exists $item->{'damaged'} and
1774 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1775 $item->{'damaged'} = 0;
1777 if (exists $item->{'itemlost'} and
1778 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1779 $item->{'itemlost'} = 0;
1781 if (exists $item->{'wthdrawn'} and
1782 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1783 $item->{'wthdrawn'} = 0;
1787 =head2 _get_single_item_column
1791 _get_single_item_column($column, $itemnumber);
1795 Retrieves the value of a single column from an C<items>
1796 row specified by C<$itemnumber>.
1800 sub _get_single_item_column {
1802 my $itemnumber = shift;
1804 my $dbh = C4::Context->dbh;
1805 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1806 $sth->execute($itemnumber);
1807 my ($value) = $sth->fetchrow();
1811 =head2 _calc_items_cn_sort
1815 _calc_items_cn_sort($item, $source_values);
1819 Helper routine to calculate C<items.cn_sort>.
1823 sub _calc_items_cn_sort {
1825 my $source_values = shift;
1827 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1830 =head2 _set_defaults_for_add
1834 _set_defaults_for_add($item_hash);
1838 Given an item hash representing an item to be added, set
1839 correct default values for columns whose default value
1840 is not handled by the DBMS. This includes the following
1847 C<items.dateaccessioned>
1869 sub _set_defaults_for_add {
1871 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1872 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1875 =head2 _koha_new_item
1879 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1883 Perform the actual insert into the C<items> table.
1887 sub _koha_new_item {
1888 my ( $item, $barcode ) = @_;
1889 my $dbh=C4::Context->dbh;
1892 "INSERT INTO items SET
1894 biblioitemnumber = ?,
1896 dateaccessioned = ?,
1900 replacementprice = ?,
1901 replacementpricedate = NOW(),
1902 datelastborrowed = ?,
1903 datelastseen = NOW(),
1926 more_subfields_xml = ?,
1929 my $sth = $dbh->prepare($query);
1931 $item->{'biblionumber'},
1932 $item->{'biblioitemnumber'},
1934 $item->{'dateaccessioned'},
1935 $item->{'booksellerid'},
1936 $item->{'homebranch'},
1938 $item->{'replacementprice'},
1939 $item->{datelastborrowed},
1941 $item->{'notforloan'},
1943 $item->{'itemlost'},
1944 $item->{'wthdrawn'},
1945 $item->{'itemcallnumber'},
1946 $item->{'restricted'},
1947 $item->{'itemnotes'},
1948 $item->{'holdingbranch'},
1950 $item->{'location'},
1953 $item->{'renewals'},
1954 $item->{'reserves'},
1955 $item->{'items.cn_source'},
1956 $item->{'items.cn_sort'},
1959 $item->{'materials'},
1961 $item->{'enumchron'},
1962 $item->{'more_subfields_xml'},
1963 $item->{'copynumber'},
1965 my $itemnumber = $dbh->{'mysql_insertid'};
1966 if ( defined $sth->errstr ) {
1967 $error.="ERROR in _koha_new_item $query".$sth->errstr;
1970 return ( $itemnumber, $error );
1973 =head2 _koha_modify_item
1977 my ($itemnumber,$error) =_koha_modify_item( $item );
1981 Perform the actual update of the C<items> row. Note that this
1982 routine accepts a hashref specifying the columns to update.
1986 sub _koha_modify_item {
1988 my $dbh=C4::Context->dbh;
1991 my $query = "UPDATE items SET ";
1993 for my $key ( keys %$item ) {
1995 push @bind, $item->{$key};
1998 $query .= " WHERE itemnumber=?";
1999 push @bind, $item->{'itemnumber'};
2000 my $sth = C4::Context->dbh->prepare($query);
2001 $sth->execute(@bind);
2002 if ( C4::Context->dbh->errstr ) {
2003 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2007 return ($item->{'itemnumber'},$error);
2010 =head2 _koha_delete_item
2014 _koha_delete_item( $dbh, $itemnum );
2018 Internal function to delete an item record from the koha tables
2022 sub _koha_delete_item {
2023 my ( $dbh, $itemnum ) = @_;
2025 # save the deleted item to deleteditems table
2026 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2027 $sth->execute($itemnum);
2028 my $data = $sth->fetchrow_hashref();
2030 my $query = "INSERT INTO deleteditems SET ";
2032 foreach my $key ( keys %$data ) {
2033 $query .= "$key = ?,";
2034 push( @bind, $data->{$key} );
2037 $sth = $dbh->prepare($query);
2038 $sth->execute(@bind);
2041 # delete from items table
2042 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2043 $sth->execute($itemnum);
2048 =head2 _marc_from_item_hash
2052 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2056 Given an item hash representing a complete item record,
2057 create a C<MARC::Record> object containing an embedded
2058 tag representing that item.
2060 The third, optional parameter C<$unlinked_item_subfields> is
2061 an arrayref of subfields (not mapped to C<items> fields per the
2062 framework) to be added to the MARC representation
2067 sub _marc_from_item_hash {
2069 my $frameworkcode = shift;
2070 my $unlinked_item_subfields;
2072 $unlinked_item_subfields = shift;
2075 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2076 # Also, don't emit a subfield if the underlying field is blank.
2077 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2078 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2079 : () } keys %{ $item } };
2081 my $item_marc = MARC::Record->new();
2082 foreach my $item_field (keys %{ $mungeditem }) {
2083 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
2084 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2087 my @values = split(/\s?\|\s?/, $mungeditem->{$item_field}, -1);
2088 foreach my $value (@values){
2089 if (my $field = $item_marc->field($tag)) {
2090 $field->add_subfields($subfield => $value);
2092 my $add_subfields = [];
2093 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2094 $add_subfields = $unlinked_item_subfields;
2096 $item_marc->add_fields( $tag, " ", " ", $subfield => $value, @$add_subfields);
2104 =head2 _add_item_field_to_biblio
2108 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2112 Adds the fields from a MARC record containing the
2113 representation of a Koha item record to the MARC
2114 biblio record. The input C<$item_marc> record
2115 is expect to contain just one field, the embedded
2116 item information field.
2120 sub _add_item_field_to_biblio {
2121 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2123 my $biblio_marc = GetMarcBiblio($biblionumber);
2124 foreach my $field ($item_marc->fields()) {
2125 $biblio_marc->append_fields($field);
2128 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2131 =head2 _replace_item_field_in_biblio
2135 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2139 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2140 representation of the item, examine the biblio MARC
2141 for the corresponding tag for that item and
2142 replace it with the tag from C<$item_marc>.
2146 sub _replace_item_field_in_biblio {
2147 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2148 my $dbh = C4::Context->dbh;
2150 # get complete MARC record & replace the item field by the new one
2151 my $completeRecord = GetMarcBiblio($biblionumber);
2152 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2153 my $itemField = $ItemRecord->field($itemtag);
2154 my @items = $completeRecord->field($itemtag);
2157 if ($_->subfield($itemsubfield) eq $itemnumber) {
2158 $_->replace_with($itemField);
2164 # If we haven't found the matching field,
2165 # just add it. However, this means that
2166 # there is likely a bug.
2167 $completeRecord->append_fields($itemField);
2171 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2174 =head2 _repack_item_errors
2176 Add an error message hash generated by C<CheckItemPreSave>
2177 to a list of errors.
2181 sub _repack_item_errors {
2182 my $item_sequence_num = shift;
2183 my $item_ref = shift;
2184 my $error_ref = shift;
2186 my @repacked_errors = ();
2188 foreach my $error_code (sort keys %{ $error_ref }) {
2189 my $repacked_error = {};
2190 $repacked_error->{'item_sequence'} = $item_sequence_num;
2191 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2192 $repacked_error->{'error_code'} = $error_code;
2193 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2194 push @repacked_errors, $repacked_error;
2197 return @repacked_errors;
2200 =head2 _get_unlinked_item_subfields
2204 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2210 sub _get_unlinked_item_subfields {
2211 my $original_item_marc = shift;
2212 my $frameworkcode = shift;
2214 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2216 # assume that this record has only one field, and that that
2217 # field contains only the item information
2219 my @fields = $original_item_marc->fields();
2220 if ($#fields > -1) {
2221 my $field = $fields[0];
2222 my $tag = $field->tag();
2223 foreach my $subfield ($field->subfields()) {
2224 if (defined $subfield->[1] and
2225 $subfield->[1] ne '' and
2226 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2227 push @$subfields, $subfield->[0] => $subfield->[1];
2234 =head2 _get_unlinked_subfields_xml
2238 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2244 sub _get_unlinked_subfields_xml {
2245 my $unlinked_item_subfields = shift;
2248 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2249 my $marc = MARC::Record->new();
2250 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2251 # used in the framework
2252 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2253 $marc->encoding("UTF-8");
2254 $xml = $marc->as_xml("USMARC");
2260 =head2 _parse_unlinked_item_subfields_from_xml
2264 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2270 sub _parse_unlinked_item_subfields_from_xml {
2273 return unless defined $xml and $xml ne "";
2274 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2275 my $unlinked_subfields = [];
2276 my @fields = $marc->fields();
2277 if ($#fields > -1) {
2278 foreach my $subfield ($fields[0]->subfields()) {
2279 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2282 return $unlinked_subfields;