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
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #use warnings; FIXME - Bug 2505
27 use C4::Dates qw/format_date format_date_in_iso/;
36 use vars qw($VERSION @ISA @EXPORT);
42 @ISA = qw( Exporter );
65 GetItemsByBiblioitemnumber
68 GetItemnumberFromBarcode
79 C4::Items - item management functions
83 This module contains an API for manipulating item
84 records in Koha, and is used by cataloguing, circulation,
85 acquisitions, and serials management.
87 A Koha item record is stored in two places: the
88 items table and embedded in a MARC tag in the XML
89 version of the associated bib record in C<biblioitems.marcxml>.
90 This is done to allow the item information to be readily
91 indexed (e.g., by Zebra), but means that each item
92 modification transaction must keep the items table
93 and the MARC XML in sync at all times.
95 Consequently, all code that creates, modifies, or deletes
96 item records B<must> use an appropriate function from
97 C<C4::Items>. If no existing function is suitable, it is
98 better to add one to C<C4::Items> than to use add
99 one-off SQL statements to add or modify items.
101 The items table will be considered authoritative. In other
102 words, if there is ever a discrepancy between the items
103 table and the MARC XML, the items table should be considered
106 =head1 HISTORICAL NOTE
108 Most of the functions in C<C4::Items> were originally in
109 the C<C4::Biblio> module.
111 =head1 CORE EXPORTED FUNCTIONS
113 The following functions are meant for use by users
122 $item = GetItem($itemnumber,$barcode,$serial);
126 Return item information, for a given itemnumber or barcode.
127 The return value is a hashref mapping item column
128 names to values. If C<$serial> is true, include serial publication data.
133 my ($itemnumber,$barcode, $serial) = @_;
134 my $dbh = C4::Context->dbh;
137 my $sth = $dbh->prepare("
139 WHERE itemnumber = ?");
140 $sth->execute($itemnumber);
141 $data = $sth->fetchrow_hashref;
143 my $sth = $dbh->prepare("
147 $sth->execute($barcode);
148 $data = $sth->fetchrow_hashref;
151 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
152 $ssth->execute($data->{'itemnumber'}) ;
153 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
154 warn $data->{'serialseq'} , $data->{'publisheddate'};
156 #if we don't have an items.itype, use biblioitems.itemtype.
157 if( ! $data->{'itype'} ) {
158 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
159 $sth->execute($data->{'biblionumber'});
160 ($data->{'itype'}) = $sth->fetchrow_array;
169 CartToShelf($itemnumber);
173 Set the current shelving location of the item record
174 to its stored permanent shelving location. This is
175 primarily used to indicate when an item whose current
176 location is a special processing ('PROC') or shelving cart
177 ('CART') location is back in the stacks.
182 my ( $itemnumber ) = @_;
184 unless ( $itemnumber ) {
185 croak "FAILED CartToShelf() - no itemnumber supplied";
188 my $item = GetItem($itemnumber);
189 $item->{location} = $item->{permanent_location};
190 ModItem($item, undef, $itemnumber);
193 =head2 AddItemFromMarc
197 my ($biblionumber, $biblioitemnumber, $itemnumber)
198 = AddItemFromMarc($source_item_marc, $biblionumber);
202 Given a MARC::Record object containing an embedded item
203 record and a biblionumber, create a new item record.
207 sub AddItemFromMarc {
208 my ( $source_item_marc, $biblionumber ) = @_;
209 my $dbh = C4::Context->dbh;
211 # parse item hash from MARC
212 my $frameworkcode = GetFrameworkCode( $biblionumber );
213 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
215 my $localitemmarc=MARC::Record->new;
216 $localitemmarc->append_fields($source_item_marc->field($itemtag));
217 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode ,'items');
218 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
219 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
226 my ($biblionumber, $biblioitemnumber, $itemnumber)
227 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
231 Given a hash containing item column names as keys,
232 create a new Koha item record.
234 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
235 do not need to be supplied for general use; they exist
236 simply to allow them to be picked up from AddItemFromMarc.
238 The final optional parameter, C<$unlinked_item_subfields>, contains
239 an arrayref containing subfields present in the original MARC
240 representation of the item (e.g., from the item editor) that are
241 not mapped to C<items> columns directly but should instead
242 be stored in C<items.more_subfields_xml> and included in
243 the biblio items tag for display and indexing.
249 my $biblionumber = shift;
251 my $dbh = @_ ? shift : C4::Context->dbh;
252 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
253 my $unlinked_item_subfields;
255 $unlinked_item_subfields = shift
258 # needs old biblionumber and biblioitemnumber
259 $item->{'biblionumber'} = $biblionumber;
260 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
261 $sth->execute( $item->{'biblionumber'} );
262 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
264 _set_defaults_for_add($item);
265 _set_derived_columns_for_add($item);
266 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
267 # FIXME - checks here
268 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
269 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
270 $itype_sth->execute( $item->{'biblionumber'} );
271 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
274 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
275 $item->{'itemnumber'} = $itemnumber;
277 # create MARC tag representing item and add to bib
278 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
279 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
281 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
283 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
286 =head2 AddItemBatchFromMarc
290 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, $biblionumber, $biblioitemnumber, $frameworkcode);
294 Efficiently create item records from a MARC biblio record with
295 embedded item fields. This routine is suitable for batch jobs.
297 This API assumes that the bib record has already been
298 saved to the C<biblio> and C<biblioitems> tables. It does
299 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
300 are populated, but it will do so via a call to ModBibiloMarc.
302 The goal of this API is to have a similar effect to using AddBiblio
303 and AddItems in succession, but without inefficient repeated
304 parsing of the MARC XML bib record.
306 This function returns an arrayref of new itemsnumbers and an arrayref of item
307 errors encountered during the processing. Each entry in the errors
308 list is a hashref containing the following keys:
314 Sequence number of original item tag in the MARC record.
318 Item barcode, provide to assist in the construction of
319 useful error messages.
321 =item error_condition
323 Code representing the error condition. Can be 'duplicate_barcode',
324 'invalid_homebranch', or 'invalid_holdingbranch'.
326 =item error_information
328 Additional information appropriate to the error condition.
334 sub AddItemBatchFromMarc {
335 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
337 my @itemnumbers = ();
339 my $dbh = C4::Context->dbh;
341 # loop through the item tags and start creating items
342 my @bad_item_fields = ();
343 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
344 my $item_sequence_num = 0;
345 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
346 $item_sequence_num++;
347 # we take the item field and stick it into a new
348 # MARC record -- this is required so far because (FIXME)
349 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
350 # and there is no TransformMarcFieldToKoha
351 my $temp_item_marc = MARC::Record->new();
352 $temp_item_marc->append_fields($item_field);
354 # add biblionumber and biblioitemnumber
355 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
356 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
357 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
358 $item->{'biblionumber'} = $biblionumber;
359 $item->{'biblioitemnumber'} = $biblioitemnumber;
361 # check for duplicate barcode
362 my %item_errors = CheckItemPreSave($item);
364 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
365 push @bad_item_fields, $item_field;
369 _set_defaults_for_add($item);
370 _set_derived_columns_for_add($item);
371 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
372 warn $error if $error;
373 push @itemnumbers, $itemnumber; # FIXME not checking error
374 $item->{'itemnumber'} = $itemnumber;
376 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
378 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
379 $item_field->replace_with($new_item_marc->field($itemtag));
382 # remove any MARC item fields for rejected items
383 foreach my $item_field (@bad_item_fields) {
384 $record->delete_field($item_field);
387 # update the MARC biblio
388 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
390 return (\@itemnumbers, \@errors);
393 =head2 ModItemFromMarc
397 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
401 This function updates an item record based on a supplied
402 C<MARC::Record> object containing an embedded item field.
403 This API is meant for the use of C<additem.pl>; for
404 other purposes, C<ModItem> should be used.
406 This function uses the hash %default_values_for_mod_from_marc,
407 which contains default values for item fields to
408 apply when modifying an item. This is needed beccause
409 if an item field's value is cleared, TransformMarcToKoha
410 does not include the column in the
411 hash that's passed to ModItem, which without
412 use of this hash makes it impossible to clear
413 an item field's value. See bug 2466.
415 Note that only columns that can be directly
416 changed from the cataloging and serials
417 item editors are included in this hash.
421 my %default_values_for_mod_from_marc = (
423 booksellerid => undef,
425 'items.cn_source' => undef,
428 dateaccessioned => undef,
430 holdingbranch => undef,
432 itemcallnumber => undef,
441 replacementprice => undef,
442 replacementpricedate => undef,
449 sub ModItemFromMarc {
450 my $item_marc = shift;
451 my $biblionumber = shift;
452 my $itemnumber = shift;
454 my $dbh = C4::Context->dbh;
455 my $frameworkcode = GetFrameworkCode( $biblionumber );
456 my ($itemtag,$itemsubfield)=GetMarcFromKohaField("items.itemnumber",$frameworkcode);
458 my $localitemmarc=MARC::Record->new;
459 $localitemmarc->append_fields($item_marc->field($itemtag));
460 my $item = &TransformMarcToKoha( $dbh, $localitemmarc, $frameworkcode, 'items');
461 foreach my $item_field (keys %default_values_for_mod_from_marc) {
462 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
464 my $unlinked_item_subfields = _get_unlinked_item_subfields($localitemmarc, $frameworkcode);
466 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
473 ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]);
477 Change one or more columns in an item record and update
478 the MARC representation of the item.
480 The first argument is a hashref mapping from item column
481 names to the new values. The second and third arguments
482 are the biblionumber and itemnumber, respectively.
484 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
485 an arrayref containing subfields present in the original MARC
486 representation of the item (e.g., from the item editor) that are
487 not mapped to C<items> columns directly but should instead
488 be stored in C<items.more_subfields_xml> and included in
489 the biblio items tag for display and indexing.
491 If one of the changed columns is used to calculate
492 the derived value of a column such as C<items.cn_sort>,
493 this routine will perform the necessary calculation
500 my $biblionumber = shift;
501 my $itemnumber = shift;
503 # if $biblionumber is undefined, get it from the current item
504 unless (defined $biblionumber) {
505 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
508 my $dbh = @_ ? shift : C4::Context->dbh;
509 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
511 my $unlinked_item_subfields;
513 $unlinked_item_subfields = shift;
514 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
517 $item->{'itemnumber'} = $itemnumber or return undef;
518 _set_derived_columns_for_mod($item);
519 _do_column_fixes_for_mod($item);
522 # attempt to change itemnumber
523 # attempt to change biblionumber (if we want
524 # an API to relink an item to a different bib,
525 # it should be a separate function)
528 _koha_modify_item($item);
530 # update biblio MARC XML
531 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
533 unless (defined $unlinked_item_subfields) {
534 $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
536 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields)
537 or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
539 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
540 ($new_item_marc eq '0') and die "$new_item_marc is '0', not hashref"; # logaction line would crash anyway
541 logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
544 =head2 ModItemTransfer
548 ModItemTransfer($itenumber, $frombranch, $tobranch);
552 Marks an item as being transferred from one branch
557 sub ModItemTransfer {
558 my ( $itemnumber, $frombranch, $tobranch ) = @_;
560 my $dbh = C4::Context->dbh;
562 #new entry in branchtransfers....
563 my $sth = $dbh->prepare(
564 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
565 VALUES (?, ?, NOW(), ?)");
566 $sth->execute($itemnumber, $frombranch, $tobranch);
568 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
569 ModDateLastSeen($itemnumber);
573 =head2 ModDateLastSeen
577 ModDateLastSeen($itemnum);
581 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
582 C<$itemnum> is the item number
586 sub ModDateLastSeen {
587 my ($itemnumber) = @_;
589 my $today = C4::Dates->new();
590 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
597 DelItem($biblionumber, $itemnumber);
601 Exported function (core API) for deleting an item record in Koha.
606 my ( $dbh, $biblionumber, $itemnumber ) = @_;
608 # FIXME check the item has no current issues
610 _koha_delete_item( $dbh, $itemnumber );
612 # get the MARC record
613 my $record = GetMarcBiblio($biblionumber);
614 my $frameworkcode = GetFrameworkCode($biblionumber);
617 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
618 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
620 #search item field code
621 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
622 my @fields = $record->field($itemtag);
624 # delete the item specified
625 foreach my $field (@fields) {
626 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
627 $record->delete_field($field);
630 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
631 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
634 =head2 CheckItemPreSave
638 my $item_ref = TransformMarcToKoha($marc, 'items');
640 my %errors = CheckItemPreSave($item_ref);
641 if (exists $errors{'duplicate_barcode'}) {
642 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
643 } elsif (exists $errors{'invalid_homebranch'}) {
644 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
645 } elsif (exists $errors{'invalid_holdingbranch'}) {
646 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
653 Given a hashref containing item fields, determine if it can be
654 inserted or updated in the database. Specifically, checks for
655 database integrity issues, and returns a hash containing any
656 of the following keys, if applicable.
660 =item duplicate_barcode
662 Barcode, if it duplicates one already found in the database.
664 =item invalid_homebranch
666 Home branch, if not defined in branches table.
668 =item invalid_holdingbranch
670 Holding branch, if not defined in branches table.
674 This function does NOT implement any policy-related checks,
675 e.g., whether current operator is allowed to save an
676 item that has a given branch code.
680 sub CheckItemPreSave {
681 my $item_ref = shift;
685 # check for duplicate barcode
686 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
687 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
688 if ($existing_itemnumber) {
689 if (!exists $item_ref->{'itemnumber'} # new item
690 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
691 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
696 # check for valid home branch
697 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
698 my $branch_name = GetBranchName($item_ref->{'homebranch'});
699 unless (defined $branch_name) {
700 # relies on fact that branches.branchname is a non-NULL column,
701 # so GetBranchName returns undef only if branch does not exist
702 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
706 # check for valid holding branch
707 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
708 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
709 unless (defined $branch_name) {
710 # relies on fact that branches.branchname is a non-NULL column,
711 # so GetBranchName returns undef only if branch does not exist
712 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
720 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
722 The following functions provide various ways of
723 getting an item record, a set of item records, or
724 lists of authorized values for certain item fields.
726 Some of the functions in this group are candidates
727 for refactoring -- for example, some of the code
728 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
729 has copy-and-paste work.
737 $itemstatushash = GetItemStatus($fwkcode);
741 Returns a list of valid values for the
742 C<items.notforloan> field.
744 NOTE: does B<not> return an individual item's
747 Can be MARC dependant.
749 But basically could be can be loan or not
750 Create a status selector with the following code
752 =head3 in PERL SCRIPT
756 my $itemstatushash = getitemstatus;
758 foreach my $thisstatus (keys %$itemstatushash) {
759 my %row =(value => $thisstatus,
760 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
762 push @itemstatusloop, \%row;
764 $template->param(statusloop=>\@itemstatusloop);
772 <select name="statusloop">
773 <option value="">Default</option>
774 <!-- TMPL_LOOP name="statusloop" -->
775 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
785 # returns a reference to a hash of references to status...
788 my $dbh = C4::Context->dbh;
790 $fwk = '' unless ($fwk);
791 my ( $tag, $subfield ) =
792 GetMarcFromKohaField( "items.notforloan", $fwk );
793 if ( $tag and $subfield ) {
796 "SELECT authorised_value
797 FROM marc_subfield_structure
803 $sth->execute( $tag, $subfield, $fwk );
804 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
807 "SELECT authorised_value,lib
808 FROM authorised_values
813 $authvalsth->execute($authorisedvaluecat);
814 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
815 $itemstatus{$authorisedvalue} = $lib;
829 $itemstatus{"1"} = "Not For Loan";
833 =head2 GetItemLocation
837 $itemlochash = GetItemLocation($fwk);
841 Returns a list of valid values for the
842 C<items.location> field.
844 NOTE: does B<not> return an individual item's
847 where fwk stands for an optional framework code.
848 Create a location selector with the following code
850 =head3 in PERL SCRIPT
854 my $itemlochash = getitemlocation;
856 foreach my $thisloc (keys %$itemlochash) {
857 my $selected = 1 if $thisbranch eq $branch;
858 my %row =(locval => $thisloc,
859 selected => $selected,
860 locname => $itemlochash->{$thisloc},
862 push @itemlocloop, \%row;
864 $template->param(itemlocationloop => \@itemlocloop);
872 <select name="location">
873 <option value="">Default</option>
874 <!-- TMPL_LOOP name="itemlocationloop" -->
875 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
883 sub GetItemLocation {
885 # returns a reference to a hash of references to location...
888 my $dbh = C4::Context->dbh;
890 $fwk = '' unless ($fwk);
891 my ( $tag, $subfield ) =
892 GetMarcFromKohaField( "items.location", $fwk );
893 if ( $tag and $subfield ) {
896 "SELECT authorised_value
897 FROM marc_subfield_structure
902 $sth->execute( $tag, $subfield, $fwk );
903 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
906 "SELECT authorised_value,lib
907 FROM authorised_values
911 $authvalsth->execute($authorisedvaluecat);
912 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
913 $itemlocation{$authorisedvalue} = $lib;
915 return \%itemlocation;
927 $itemlocation{"1"} = "Not For Loan";
928 return \%itemlocation;
935 $items = GetLostItems( $where, $orderby );
939 This function gets a list of lost items.
945 C<$where> is a hashref. it containts a field of the items table as key
946 and the value to match as value. For example:
948 { barcode => 'abc123',
949 homebranch => 'CPL', }
951 C<$orderby> is a field of the items table by which the resultset
956 C<$items> is a reference to an array full of hashrefs with columns
957 from the "items" table as keys.
959 =item usage in the perl script:
961 my $where = { barcode => '0001548' };
962 my $items = GetLostItems( $where, "homebranch" );
963 $template->param( itemsloop => $items );
970 # Getting input args.
973 my $dbh = C4::Context->dbh;
978 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
979 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
980 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
982 authorised_values.category = 'LOST'
983 AND itemlost IS NOT NULL
986 my @query_parameters;
987 foreach my $key (keys %$where) {
988 $query .= " AND $key LIKE ?";
989 push @query_parameters, "%$where->{$key}%";
991 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
993 if ( defined $orderby && grep($orderby, @ordervalues)) {
994 $query .= ' ORDER BY '.$orderby;
997 my $sth = $dbh->prepare($query);
998 $sth->execute( @query_parameters );
1000 while ( my $row = $sth->fetchrow_hashref ){
1006 =head2 GetItemsForInventory
1010 $itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size, $statushash);
1014 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1016 The sub returns a reference to a list of hashes, each containing
1017 itemnumber, author, title, barcode, item callnumber, and date last
1018 seen. It is ordered by callnumber then title.
1020 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
1021 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1022 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1023 $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.
1027 sub GetItemsForInventory {
1028 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branch, $offset, $size, $statushash ) = @_;
1029 my $dbh = C4::Context->dbh;
1030 my ( @bind_params, @where_strings );
1032 my $query = <<'END_SQL';
1033 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
1035 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1036 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1039 for my $authvfield (keys %$statushash){
1040 if ( scalar @{$statushash->{$authvfield}} > 0 ){
1041 my $joinedvals = join ',', @{$statushash->{$authvfield}};
1042 push @where_strings, "$authvfield in (" . $joinedvals . ")";
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;
1078 if ( $ignoreissued) {
1079 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1080 push @where_strings, 'issues.date_due IS NULL';
1083 if ( @where_strings ) {
1085 $query .= join ' AND ', @where_strings;
1087 $query .= ' ORDER BY items.cn_sort, itemcallnumber, title';
1088 my $sth = $dbh->prepare($query);
1089 $sth->execute( @bind_params );
1093 while ( my $row = $sth->fetchrow_hashref ) {
1094 $offset-- if ($offset);
1095 $row->{datelastseen}=format_date($row->{datelastseen});
1096 if ( ( !$offset ) && $size ) {
1097 push @results, $row;
1104 =head2 GetItemsCount
1107 $count = &GetItemsCount( $biblionumber);
1111 This function return count of item with $biblionumber
1116 my ( $biblionumber ) = @_;
1117 my $dbh = C4::Context->dbh;
1118 my $query = "SELECT count(*)
1120 WHERE biblionumber=?";
1121 my $sth = $dbh->prepare($query);
1122 $sth->execute($biblionumber);
1123 my $count = $sth->fetchrow;
1127 =head2 GetItemInfosOf
1131 GetItemInfosOf(@itemnumbers);
1137 sub GetItemInfosOf {
1138 my @itemnumbers = @_;
1143 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1145 return get_infos_of( $query, 'itemnumber' );
1148 =head2 GetItemsByBiblioitemnumber
1152 GetItemsByBiblioitemnumber($biblioitemnumber);
1156 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1157 Called by C<C4::XISBN>
1161 sub GetItemsByBiblioitemnumber {
1162 my ( $bibitem ) = @_;
1163 my $dbh = C4::Context->dbh;
1164 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1165 # Get all items attached to a biblioitem
1168 $sth->execute($bibitem) || die $sth->errstr;
1169 while ( my $data = $sth->fetchrow_hashref ) {
1170 # Foreach item, get circulation information
1171 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1172 WHERE itemnumber = ?
1173 AND issues.borrowernumber = borrowers.borrowernumber"
1175 $sth2->execute( $data->{'itemnumber'} );
1176 if ( my $data2 = $sth2->fetchrow_hashref ) {
1177 # if item is out, set the due date and who it is out too
1178 $data->{'date_due'} = $data2->{'date_due'};
1179 $data->{'cardnumber'} = $data2->{'cardnumber'};
1180 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1183 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1184 $data->{'date_due'} = '';
1186 # Find the last 3 people who borrowed this item.
1187 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1188 AND old_issues.borrowernumber = borrowers.borrowernumber
1189 ORDER BY returndate desc,timestamp desc LIMIT 3";
1190 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1191 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1193 while ( my $data2 = $sth2->fetchrow_hashref ) {
1194 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1195 $data->{"card$i2"} = $data2->{'cardnumber'};
1196 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1199 push(@results,$data);
1208 @results = GetItemsInfo($biblionumber, $type);
1212 Returns information about books with the given biblionumber.
1214 C<$type> may be either C<intra> or anything else. If it is not set to
1215 C<intra>, then the search will exclude lost, very overdue, and
1218 C<GetItemsInfo> returns a list of references-to-hash. Each element
1219 contains a number of keys. Most of them are table items from the
1220 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1221 Koha database. Other keys include:
1225 =item C<$data-E<gt>{branchname}>
1227 The name (not the code) of the branch to which the book belongs.
1229 =item C<$data-E<gt>{datelastseen}>
1231 This is simply C<items.datelastseen>, except that while the date is
1232 stored in YYYY-MM-DD format in the database, here it is converted to
1233 DD/MM/YYYY format. A NULL date is returned as C<//>.
1235 =item C<$data-E<gt>{datedue}>
1237 =item C<$data-E<gt>{class}>
1239 This is the concatenation of C<biblioitems.classification>, the book's
1240 Dewey code, and C<biblioitems.subclass>.
1242 =item C<$data-E<gt>{ocount}>
1244 I think this is the number of copies of the book available.
1246 =item C<$data-E<gt>{order}>
1248 If this is set, it is set to C<One Order>.
1255 my ( $biblionumber, $type ) = @_;
1256 my $dbh = C4::Context->dbh;
1257 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1263 biblioitems.itemtype,
1266 biblioitems.publicationyear,
1267 biblioitems.publishercode,
1268 biblioitems.volumedate,
1269 biblioitems.volumedesc,
1272 items.notforloan as itemnotforloan,
1273 itemtypes.description,
1276 LEFT JOIN branches ON items.homebranch = branches.branchcode
1277 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1278 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1279 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1280 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1281 $query .= " WHERE items.biblionumber = ? ORDER BY branches.branchname,items.dateaccessioned desc" ;
1282 my $sth = $dbh->prepare($query);
1283 $sth->execute($biblionumber);
1288 my $isth = $dbh->prepare(
1289 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1290 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1291 WHERE itemnumber = ?"
1293 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1294 while ( my $data = $sth->fetchrow_hashref ) {
1297 $isth->execute( $data->{'itemnumber'} );
1298 if ( my $idata = $isth->fetchrow_hashref ) {
1299 $data->{borrowernumber} = $idata->{borrowernumber};
1300 $data->{cardnumber} = $idata->{cardnumber};
1301 $data->{surname} = $idata->{surname};
1302 $data->{firstname} = $idata->{firstname};
1303 $datedue = $idata->{'date_due'};
1304 if (C4::Context->preference("IndependantBranches")){
1305 my $userenv = C4::Context->userenv;
1306 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1307 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1311 if ( $data->{'serial'}) {
1312 $ssth->execute($data->{'itemnumber'}) ;
1313 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1316 if ( $datedue eq '' ) {
1317 my ( $restype, $reserves ) =
1318 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1319 # Previous conditional check with if ($restype) is not needed because a true
1320 # result for one item will result in subsequent items defaulting to this true
1322 $count_reserves = $restype;
1324 #get branch information.....
1325 my $bsth = $dbh->prepare(
1326 "SELECT * FROM branches WHERE branchcode = ?
1329 $bsth->execute( $data->{'holdingbranch'} );
1330 if ( my $bdata = $bsth->fetchrow_hashref ) {
1331 $data->{'branchname'} = $bdata->{'branchname'};
1333 $data->{'datedue'} = $datedue;
1334 $data->{'count_reserves'} = $count_reserves;
1336 # get notforloan complete status if applicable
1337 my $sthnflstatus = $dbh->prepare(
1338 'SELECT authorised_value
1339 FROM marc_subfield_structure
1340 WHERE kohafield="items.notforloan"
1344 $sthnflstatus->execute;
1345 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1346 if ($authorised_valuecode) {
1347 $sthnflstatus = $dbh->prepare(
1348 "SELECT lib FROM authorised_values
1350 AND authorised_value=?"
1352 $sthnflstatus->execute( $authorised_valuecode,
1353 $data->{itemnotforloan} );
1354 my ($lib) = $sthnflstatus->fetchrow;
1355 $data->{notforloanvalue} = $lib;
1357 $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes'));
1359 # my stack procedures
1360 my $stackstatus = $dbh->prepare(
1361 'SELECT authorised_value
1362 FROM marc_subfield_structure
1363 WHERE kohafield="items.stack"
1366 $stackstatus->execute;
1368 ($authorised_valuecode) = $stackstatus->fetchrow;
1369 if ($authorised_valuecode) {
1370 $stackstatus = $dbh->prepare(
1372 FROM authorised_values
1374 AND authorised_value=?
1377 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1378 my ($lib) = $stackstatus->fetchrow;
1379 $data->{stack} = $lib;
1381 # Find the last 3 people who borrowed this item.
1382 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1383 WHERE itemnumber = ?
1384 AND old_issues.borrowernumber = borrowers.borrowernumber
1385 ORDER BY returndate DESC
1387 $sth2->execute($data->{'itemnumber'});
1389 while (my $data2 = $sth2->fetchrow_hashref()) {
1390 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1391 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1392 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1396 $results[$i] = $data;
1400 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1406 =head2 GetLastAcquisitions
1410 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 'itemtypes' => ('BK','BD')}, 10);
1416 sub GetLastAcquisitions {
1417 my ($data,$max) = @_;
1419 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1421 my $number_of_branches = @{$data->{branches}};
1422 my $number_of_itemtypes = @{$data->{itemtypes}};
1425 my @where = ('WHERE 1 ');
1426 $number_of_branches and push @where
1427 , 'AND holdingbranch IN ('
1428 , join(',', ('?') x $number_of_branches )
1432 $number_of_itemtypes and push @where
1433 , "AND $itemtype IN ("
1434 , join(',', ('?') x $number_of_itemtypes )
1438 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1439 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1440 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1442 GROUP BY biblio.biblionumber
1443 ORDER BY dateaccessioned DESC LIMIT $max";
1445 my $dbh = C4::Context->dbh;
1446 my $sth = $dbh->prepare($query);
1448 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1451 while( my $row = $sth->fetchrow_hashref){
1452 push @results, {date => $row->{dateaccessioned}
1453 , biblionumber => $row->{biblionumber}
1454 , title => $row->{title}};
1460 =head2 get_itemnumbers_of
1464 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1468 Given a list of biblionumbers, return the list of corresponding itemnumbers
1469 for each biblionumber.
1471 Return a reference on a hash where keys are biblionumbers and values are
1472 references on array of itemnumbers.
1476 sub get_itemnumbers_of {
1477 my @biblionumbers = @_;
1479 my $dbh = C4::Context->dbh;
1485 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1487 my $sth = $dbh->prepare($query);
1488 $sth->execute(@biblionumbers);
1492 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1493 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1496 return \%itemnumbers_of;
1499 =head2 GetItemnumberFromBarcode
1503 $result = GetItemnumberFromBarcode($barcode);
1509 sub GetItemnumberFromBarcode {
1511 my $dbh = C4::Context->dbh;
1514 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1515 $rq->execute($barcode);
1516 my ($result) = $rq->fetchrow;
1520 =head3 get_item_authorised_values
1522 find the types and values for all authorised values assigned to this item.
1527 returns: a hashref malling the authorised value to the value set for this itemnumber
1529 $authorised_values = {
1535 'RESTRICTED' => undef,
1538 'branches' => 'CPL',
1539 'cn_source' => undef,
1540 'itemtypes' => 'SER',
1543 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1547 sub get_item_authorised_values {
1548 my $itemnumber = shift;
1550 # assume that these entries in the authorised_value table are item level.
1551 my $query = q(SELECT distinct authorised_value, kohafield
1552 FROM marc_subfield_structure
1553 WHERE kohafield like 'item%'
1554 AND authorised_value != '' );
1556 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1557 my $iteminfo = GetItem( $itemnumber );
1558 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1560 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1561 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1562 $field =~ s/^items\.//;
1563 if ( exists $iteminfo->{ $field } ) {
1564 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1567 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1571 =head3 get_authorised_value_images
1573 find a list of icons that are appropriate for display based on the
1574 authorised values for a biblio.
1576 parameters: listref of authorised values, such as comes from
1577 get_item_authorised_values or
1578 from C4::Biblio::get_biblio_authorised_values
1580 returns: listref of hashrefs for each image. Each hashref looks like
1583 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1588 Notes: Currently, I put on the full path to the images on the staff
1589 side. This should either be configurable or not done at all. Since I
1590 have to deal with 'intranet' or 'opac' in
1591 get_biblio_authorised_values, perhaps I should be passing it in.
1595 sub get_authorised_value_images {
1596 my $authorised_values = shift;
1600 my $authorised_value_list = GetAuthorisedValues();
1601 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1602 foreach my $this_authorised_value ( @$authorised_value_list ) {
1603 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1604 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1605 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1606 if ( defined $this_authorised_value->{'imageurl'} ) {
1607 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1608 label => $this_authorised_value->{'lib'},
1609 category => $this_authorised_value->{'category'},
1610 value => $this_authorised_value->{'authorised_value'}, };
1615 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1620 =head1 LIMITED USE FUNCTIONS
1622 The following functions, while part of the public API,
1623 are not exported. This is generally because they are
1624 meant to be used by only one script for a specific
1625 purpose, and should not be used in any other context
1626 without careful thought.
1634 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1638 Returns MARC::Record of the item passed in parameter.
1639 This function is meant for use only in C<cataloguing/additem.pl>,
1640 where it is needed to support that script's MARC-like
1646 my ( $biblionumber, $itemnumber ) = @_;
1648 # GetMarcItem has been revised so that it does the following:
1649 # 1. Gets the item information from the items table.
1650 # 2. Converts it to a MARC field for storage in the bib record.
1652 # The previous behavior was:
1653 # 1. Get the bib record.
1654 # 2. Return the MARC tag corresponding to the item record.
1656 # The difference is that one treats the items row as authoritative,
1657 # while the other treats the MARC representation as authoritative
1658 # under certain circumstances.
1660 my $itemrecord = GetItem($itemnumber);
1662 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1663 # Also, don't emit a subfield if the underlying field is blank.
1666 return Item2Marc($itemrecord,$biblionumber);
1670 my ($itemrecord,$biblionumber)=@_;
1673 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1674 } keys %{ $itemrecord }
1676 my $itemmarc = TransformKohaToMarc($mungeditem);
1677 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",GetFrameworkCode($biblionumber)||'');
1679 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1680 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1681 foreach my $field ($itemmarc->field($itemtag)){
1682 $field->add_subfields(@$unlinked_item_subfields);
1688 =head1 PRIVATE FUNCTIONS AND VARIABLES
1690 The following functions are not meant to be called
1691 directly, but are documented in order to explain
1692 the inner workings of C<C4::Items>.
1696 =head2 %derived_columns
1698 This hash keeps track of item columns that
1699 are strictly derived from other columns in
1700 the item record and are not meant to be set
1703 Each key in the hash should be the name of a
1704 column (as named by TransformMarcToKoha). Each
1705 value should be hashref whose keys are the
1706 columns on which the derived column depends. The
1707 hashref should also contain a 'BUILDER' key
1708 that is a reference to a sub that calculates
1713 my %derived_columns = (
1714 'items.cn_sort' => {
1715 'itemcallnumber' => 1,
1716 'items.cn_source' => 1,
1717 'BUILDER' => \&_calc_items_cn_sort,
1721 =head2 _set_derived_columns_for_add
1725 _set_derived_column_for_add($item);
1729 Given an item hash representing a new item to be added,
1730 calculate any derived columns. Currently the only
1731 such column is C<items.cn_sort>.
1735 sub _set_derived_columns_for_add {
1738 foreach my $column (keys %derived_columns) {
1739 my $builder = $derived_columns{$column}->{'BUILDER'};
1740 my $source_values = {};
1741 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1742 next if $source_column eq 'BUILDER';
1743 $source_values->{$source_column} = $item->{$source_column};
1745 $builder->($item, $source_values);
1749 =head2 _set_derived_columns_for_mod
1753 _set_derived_column_for_mod($item);
1757 Given an item hash representing a new item to be modified.
1758 calculate any derived columns. Currently the only
1759 such column is C<items.cn_sort>.
1761 This routine differs from C<_set_derived_columns_for_add>
1762 in that it needs to handle partial item records. In other
1763 words, the caller of C<ModItem> may have supplied only one
1764 or two columns to be changed, so this function needs to
1765 determine whether any of the columns to be changed affect
1766 any of the derived columns. Also, if a derived column
1767 depends on more than one column, but the caller is not
1768 changing all of then, this routine retrieves the unchanged
1769 values from the database in order to ensure a correct
1774 sub _set_derived_columns_for_mod {
1777 foreach my $column (keys %derived_columns) {
1778 my $builder = $derived_columns{$column}->{'BUILDER'};
1779 my $source_values = {};
1780 my %missing_sources = ();
1781 my $must_recalc = 0;
1782 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1783 next if $source_column eq 'BUILDER';
1784 if (exists $item->{$source_column}) {
1786 $source_values->{$source_column} = $item->{$source_column};
1788 $missing_sources{$source_column} = 1;
1792 foreach my $source_column (keys %missing_sources) {
1793 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1795 $builder->($item, $source_values);
1800 =head2 _do_column_fixes_for_mod
1804 _do_column_fixes_for_mod($item);
1808 Given an item hashref containing one or more
1809 columns to modify, fix up certain values.
1810 Specifically, set to 0 any passed value
1811 of C<notforloan>, C<damaged>, C<itemlost>, or
1812 C<wthdrawn> that is either undefined or
1813 contains the empty string.
1817 sub _do_column_fixes_for_mod {
1820 if (exists $item->{'notforloan'} and
1821 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1822 $item->{'notforloan'} = 0;
1824 if (exists $item->{'damaged'} and
1825 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1826 $item->{'damaged'} = 0;
1828 if (exists $item->{'itemlost'} and
1829 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1830 $item->{'itemlost'} = 0;
1832 if (exists $item->{'wthdrawn'} and
1833 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1834 $item->{'wthdrawn'} = 0;
1836 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1837 $item->{'permanent_location'} = $item->{'location'};
1841 =head2 _get_single_item_column
1845 _get_single_item_column($column, $itemnumber);
1849 Retrieves the value of a single column from an C<items>
1850 row specified by C<$itemnumber>.
1854 sub _get_single_item_column {
1856 my $itemnumber = shift;
1858 my $dbh = C4::Context->dbh;
1859 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1860 $sth->execute($itemnumber);
1861 my ($value) = $sth->fetchrow();
1865 =head2 _calc_items_cn_sort
1869 _calc_items_cn_sort($item, $source_values);
1873 Helper routine to calculate C<items.cn_sort>.
1877 sub _calc_items_cn_sort {
1879 my $source_values = shift;
1881 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1884 =head2 _set_defaults_for_add
1888 _set_defaults_for_add($item_hash);
1892 Given an item hash representing an item to be added, set
1893 correct default values for columns whose default value
1894 is not handled by the DBMS. This includes the following
1901 C<items.dateaccessioned>
1923 sub _set_defaults_for_add {
1925 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1926 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1929 =head2 _koha_new_item
1933 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1937 Perform the actual insert into the C<items> table.
1941 sub _koha_new_item {
1942 my ( $item, $barcode ) = @_;
1943 my $dbh=C4::Context->dbh;
1946 "INSERT INTO items SET
1948 biblioitemnumber = ?,
1950 dateaccessioned = ?,
1954 replacementprice = ?,
1955 replacementpricedate = NOW(),
1956 datelastborrowed = ?,
1957 datelastseen = NOW(),
1980 more_subfields_xml = ?,
1983 my $sth = $dbh->prepare($query);
1985 $item->{'biblionumber'},
1986 $item->{'biblioitemnumber'},
1988 $item->{'dateaccessioned'},
1989 $item->{'booksellerid'},
1990 $item->{'homebranch'},
1992 $item->{'replacementprice'},
1993 $item->{datelastborrowed},
1995 $item->{'notforloan'},
1997 $item->{'itemlost'},
1998 $item->{'wthdrawn'},
1999 $item->{'itemcallnumber'},
2000 $item->{'restricted'},
2001 $item->{'itemnotes'},
2002 $item->{'holdingbranch'},
2004 $item->{'location'},
2007 $item->{'renewals'},
2008 $item->{'reserves'},
2009 $item->{'items.cn_source'},
2010 $item->{'items.cn_sort'},
2013 $item->{'materials'},
2015 $item->{'enumchron'},
2016 $item->{'more_subfields_xml'},
2017 $item->{'copynumber'},
2019 my $itemnumber = $dbh->{'mysql_insertid'};
2020 if ( defined $sth->errstr ) {
2021 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2023 return ( $itemnumber, $error );
2026 =head2 MoveItemFromBiblio
2030 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2034 Moves an item from a biblio to another
2036 Returns undef if the move failed or the biblionumber of the destination record otherwise
2038 sub MoveItemFromBiblio {
2039 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2040 my $dbh = C4::Context->dbh;
2041 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = ?");
2042 $sth->execute( $tobiblio );
2043 my ( $tobiblioitem ) = $sth->fetchrow();
2044 $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2045 my $return = $sth->execute($tobiblioitem, $tobiblio, $itemnumber, $frombiblio);
2049 my $frameworkcode = GetFrameworkCode($frombiblio);
2051 # Getting marc field for itemnumber
2052 my ($itemtag, $itemsubfield) = GetMarcFromKohaField('items.itemnumber', $frameworkcode);
2054 # Getting the record we want to move the item from
2055 my $record = GetMarcBiblio($frombiblio);
2057 # The item we want to move
2061 foreach my $fielditem ($record->field($itemtag)){
2062 # If it is the item we want to move
2063 if ($fielditem->subfield($itemsubfield) == $itemnumber) {
2066 # Then delete it from the record
2067 $record->delete_field($fielditem)
2071 # If we found an item (should always true, except in case of database-marcxml inconsistency)
2074 # Checking if the item we want to move is in an order
2075 my $order = GetOrderFromItemnumber($itemnumber);
2077 # Replacing the biblionumber within the order if necessary
2078 $order->{'biblionumber'} = $tobiblio;
2082 # Saving the modification
2083 ModBiblioMarc($record, $frombiblio, $frameworkcode);
2085 # Getting the record we want to move the item to
2086 $record = GetMarcBiblio($tobiblio);
2088 # Inserting the previously saved item
2089 $record->insert_fields_ordered($item);
2091 # Saving the modification
2092 ModBiblioMarc($record, $tobiblio, $frameworkcode);
2106 DelItemCheck($dbh, $biblionumber, $itemnumber);
2110 Exported function (core API) for deleting an item record in Koha if there no current issue.
2115 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2118 # check that there is no issue on this item before deletion.
2119 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2120 $sth->execute($itemnumber);
2122 my $onloan=$sth->fetchrow;
2125 $error = "book_on_loan"
2127 # check it doesnt have a waiting reserve
2128 $sth=$dbh->prepare("SELECT * FROM reserves WHERE found = 'W' AND itemnumber = ?");
2129 $sth->execute($itemnumber);
2130 my $reserve=$sth->fetchrow;
2132 $error = "book_reserved";
2134 DelItem($dbh, $biblionumber, $itemnumber);
2141 =head2 _koha_modify_item
2145 my ($itemnumber,$error) =_koha_modify_item( $item );
2149 Perform the actual update of the C<items> row. Note that this
2150 routine accepts a hashref specifying the columns to update.
2154 sub _koha_modify_item {
2156 my $dbh=C4::Context->dbh;
2159 my $query = "UPDATE items SET ";
2161 for my $key ( keys %$item ) {
2163 push @bind, $item->{$key};
2166 $query .= " WHERE itemnumber=?";
2167 push @bind, $item->{'itemnumber'};
2168 my $sth = C4::Context->dbh->prepare($query);
2169 $sth->execute(@bind);
2170 if ( C4::Context->dbh->errstr ) {
2171 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2174 return ($item->{'itemnumber'},$error);
2177 =head2 _koha_delete_item
2181 _koha_delete_item( $dbh, $itemnum );
2185 Internal function to delete an item record from the koha tables
2189 sub _koha_delete_item {
2190 my ( $dbh, $itemnum ) = @_;
2192 # save the deleted item to deleteditems table
2193 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2194 $sth->execute($itemnum);
2195 my $data = $sth->fetchrow_hashref();
2196 my $query = "INSERT INTO deleteditems SET ";
2198 foreach my $key ( keys %$data ) {
2199 $query .= "$key = ?,";
2200 push( @bind, $data->{$key} );
2203 $sth = $dbh->prepare($query);
2204 $sth->execute(@bind);
2206 # delete from items table
2207 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2208 $sth->execute($itemnum);
2212 =head2 _marc_from_item_hash
2216 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2220 Given an item hash representing a complete item record,
2221 create a C<MARC::Record> object containing an embedded
2222 tag representing that item.
2224 The third, optional parameter C<$unlinked_item_subfields> is
2225 an arrayref of subfields (not mapped to C<items> fields per the
2226 framework) to be added to the MARC representation
2231 sub _marc_from_item_hash {
2233 my $frameworkcode = shift;
2234 my $unlinked_item_subfields;
2236 $unlinked_item_subfields = shift;
2239 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2240 # Also, don't emit a subfield if the underlying field is blank.
2241 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2242 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2243 : () } keys %{ $item } };
2245 my $item_marc = MARC::Record->new();
2246 foreach my $item_field (keys %{ $mungeditem }) {
2247 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
2248 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2249 if (my $field = $item_marc->field($tag)) {
2250 $field->add_subfields($subfield => $mungeditem->{$item_field});
2252 my $add_subfields = [];
2253 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2254 $add_subfields = $unlinked_item_subfields;
2256 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field}, @$add_subfields);
2263 =head2 _add_item_field_to_biblio
2267 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2271 Adds the fields from a MARC record containing the
2272 representation of a Koha item record to the MARC
2273 biblio record. The input C<$item_marc> record
2274 is expect to contain just one field, the embedded
2275 item information field.
2279 sub _add_item_field_to_biblio {
2280 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2282 my $biblio_marc = GetMarcBiblio($biblionumber);
2283 foreach my $field ($item_marc->fields()) {
2284 $biblio_marc->append_fields($field);
2287 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2290 =head2 _replace_item_field_in_biblio
2294 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2298 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2299 representation of the item, examine the biblio MARC
2300 for the corresponding tag for that item and
2301 replace it with the tag from C<$item_marc>.
2305 sub _replace_item_field_in_biblio {
2306 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2307 my $dbh = C4::Context->dbh;
2309 # get complete MARC record & replace the item field by the new one
2310 my $completeRecord = GetMarcBiblio($biblionumber);
2311 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2312 my $itemField = $ItemRecord->field($itemtag);
2313 my @items = $completeRecord->field($itemtag);
2316 if ($_->subfield($itemsubfield) eq $itemnumber) {
2317 $_->replace_with($itemField);
2323 # If we haven't found the matching field,
2324 # just add it. However, this means that
2325 # there is likely a bug.
2326 $completeRecord->append_fields($itemField);
2330 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2333 =head2 _repack_item_errors
2335 Add an error message hash generated by C<CheckItemPreSave>
2336 to a list of errors.
2340 sub _repack_item_errors {
2341 my $item_sequence_num = shift;
2342 my $item_ref = shift;
2343 my $error_ref = shift;
2345 my @repacked_errors = ();
2347 foreach my $error_code (sort keys %{ $error_ref }) {
2348 my $repacked_error = {};
2349 $repacked_error->{'item_sequence'} = $item_sequence_num;
2350 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2351 $repacked_error->{'error_code'} = $error_code;
2352 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2353 push @repacked_errors, $repacked_error;
2356 return @repacked_errors;
2359 =head2 _get_unlinked_item_subfields
2363 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2369 sub _get_unlinked_item_subfields {
2370 my $original_item_marc = shift;
2371 my $frameworkcode = shift;
2373 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2375 # assume that this record has only one field, and that that
2376 # field contains only the item information
2378 my @fields = $original_item_marc->fields();
2379 if ($#fields > -1) {
2380 my $field = $fields[0];
2381 my $tag = $field->tag();
2382 foreach my $subfield ($field->subfields()) {
2383 if (defined $subfield->[1] and
2384 $subfield->[1] ne '' and
2385 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2386 push @$subfields, $subfield->[0] => $subfield->[1];
2393 =head2 _get_unlinked_subfields_xml
2397 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2403 sub _get_unlinked_subfields_xml {
2404 my $unlinked_item_subfields = shift;
2407 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2408 my $marc = MARC::Record->new();
2409 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2410 # used in the framework
2411 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2412 $marc->encoding("UTF-8");
2413 $xml = $marc->as_xml("USMARC");
2419 =head2 _parse_unlinked_item_subfields_from_xml
2423 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2429 sub _parse_unlinked_item_subfields_from_xml {
2432 return unless defined $xml and $xml ne "";
2433 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2434 my $unlinked_subfields = [];
2435 my @fields = $marc->fields();
2436 if ($#fields > -1) {
2437 foreach my $subfield ($fields[0]->subfields()) {
2438 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2441 return $unlinked_subfields;