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
26 use C4::Dates qw/format_date format_date_in_iso/;
34 use vars qw($VERSION @ISA @EXPORT);
40 @ISA = qw( Exporter );
62 GetItemsByBiblioitemnumber
65 GetItemnumberFromBarcode
76 C4::Items - item management functions
80 This module contains an API for manipulating item
81 records in Koha, and is used by cataloguing, circulation,
82 acquisitions, and serials management.
84 A Koha item record is stored in two places: the
85 items table and embedded in a MARC tag in the XML
86 version of the associated bib record in C<biblioitems.marcxml>.
87 This is done to allow the item information to be readily
88 indexed (e.g., by Zebra), but means that each item
89 modification transaction must keep the items table
90 and the MARC XML in sync at all times.
92 Consequently, all code that creates, modifies, or deletes
93 item records B<must> use an appropriate function from
94 C<C4::Items>. If no existing function is suitable, it is
95 better to add one to C<C4::Items> than to use add
96 one-off SQL statements to add or modify items.
98 The items table will be considered authoritative. In other
99 words, if there is ever a discrepancy between the items
100 table and the MARC XML, the items table should be considered
103 =head1 HISTORICAL NOTE
105 Most of the functions in C<C4::Items> were originally in
106 the C<C4::Biblio> module.
108 =head1 CORE EXPORTED FUNCTIONS
110 The following functions are meant for use by users
119 $item = GetItem($itemnumber,$barcode,$serial);
123 Return item information, for a given itemnumber or barcode.
124 The return value is a hashref mapping item column
125 names to values. If C<$serial> is true, include serial publication data.
130 my ($itemnumber,$barcode, $serial) = @_;
131 my $dbh = C4::Context->dbh;
134 my $sth = $dbh->prepare("
136 WHERE itemnumber = ?");
137 $sth->execute($itemnumber);
138 $data = $sth->fetchrow_hashref;
140 my $sth = $dbh->prepare("
144 $sth->execute($barcode);
145 $data = $sth->fetchrow_hashref;
148 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
149 $ssth->execute($data->{'itemnumber'}) ;
150 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
151 warn $data->{'serialseq'} , $data->{'publisheddate'};
153 #if we don't have an items.itype, use biblioitems.itemtype.
154 if( ! $data->{'itype'} ) {
155 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
156 $sth->execute($data->{'biblionumber'});
157 ($data->{'itype'}) = $sth->fetchrow_array;
166 CartToShelf($itemnumber);
170 Set the current shelving location of the item record
171 to its stored permanent shelving location. This is
172 primarily used to indicate when an item whose current
173 location is a special processing ('PROC') or shelving cart
174 ('CART') location is back in the stacks.
179 my ( $itemnumber ) = @_;
181 unless ( $itemnumber ) {
182 croak "FAILED CartToShelf() - no itemnumber supplied";
185 my $item = GetItem($itemnumber);
186 $item->{location} = $item->{permanent_location};
187 ModItem($item, undef, $itemnumber);
190 =head2 AddItemFromMarc
194 my ($biblionumber, $biblioitemnumber, $itemnumber)
195 = AddItemFromMarc($source_item_marc, $biblionumber);
199 Given a MARC::Record object containing an embedded item
200 record and a biblionumber, create a new item record.
204 sub AddItemFromMarc {
205 my ( $source_item_marc, $biblionumber ) = @_;
206 my $dbh = C4::Context->dbh;
208 # parse item hash from MARC
209 my $frameworkcode = GetFrameworkCode( $biblionumber );
210 my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
211 my $unlinked_item_subfields = _get_unlinked_item_subfields($source_item_marc, $frameworkcode);
212 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
219 my ($biblionumber, $biblioitemnumber, $itemnumber)
220 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
224 Given a hash containing item column names as keys,
225 create a new Koha item record.
227 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
228 do not need to be supplied for general use; they exist
229 simply to allow them to be picked up from AddItemFromMarc.
231 The final optional parameter, C<$unlinked_item_subfields>, contains
232 an arrayref containing subfields present in the original MARC
233 representation of the item (e.g., from the item editor) that are
234 not mapped to C<items> columns directly but should instead
235 be stored in C<items.more_subfields_xml> and included in
236 the biblio items tag for display and indexing.
242 my $biblionumber = shift;
244 my $dbh = @_ ? shift : C4::Context->dbh;
245 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
246 my $unlinked_item_subfields;
248 $unlinked_item_subfields = shift
251 # needs old biblionumber and biblioitemnumber
252 $item->{'biblionumber'} = $biblionumber;
253 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
254 $sth->execute( $item->{'biblionumber'} );
255 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
257 _set_defaults_for_add($item);
258 _set_derived_columns_for_add($item);
259 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
260 # FIXME - checks here
261 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
262 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
263 $itype_sth->execute( $item->{'biblionumber'} );
264 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
267 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
268 $item->{'itemnumber'} = $itemnumber;
270 # create MARC tag representing item and add to bib
271 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
272 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
274 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
276 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
279 =head2 AddItemBatchFromMarc
283 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, $biblionumber, $biblioitemnumber, $frameworkcode);
287 Efficiently create item records from a MARC biblio record with
288 embedded item fields. This routine is suitable for batch jobs.
290 This API assumes that the bib record has already been
291 saved to the C<biblio> and C<biblioitems> tables. It does
292 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
293 are populated, but it will do so via a call to ModBibiloMarc.
295 The goal of this API is to have a similar effect to using AddBiblio
296 and AddItems in succession, but without inefficient repeated
297 parsing of the MARC XML bib record.
299 This function returns an arrayref of new itemsnumbers and an arrayref of item
300 errors encountered during the processing. Each entry in the errors
301 list is a hashref containing the following keys:
307 Sequence number of original item tag in the MARC record.
311 Item barcode, provide to assist in the construction of
312 useful error messages.
314 =item error_condition
316 Code representing the error condition. Can be 'duplicate_barcode',
317 'invalid_homebranch', or 'invalid_holdingbranch'.
319 =item error_information
321 Additional information appropriate to the error condition.
327 sub AddItemBatchFromMarc {
328 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
330 my @itemnumbers = ();
332 my $dbh = C4::Context->dbh;
334 # loop through the item tags and start creating items
335 my @bad_item_fields = ();
336 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
337 my $item_sequence_num = 0;
338 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
339 $item_sequence_num++;
340 # we take the item field and stick it into a new
341 # MARC record -- this is required so far because (FIXME)
342 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
343 # and there is no TransformMarcFieldToKoha
344 my $temp_item_marc = MARC::Record->new();
345 $temp_item_marc->append_fields($item_field);
347 # add biblionumber and biblioitemnumber
348 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
349 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
350 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
351 $item->{'biblionumber'} = $biblionumber;
352 $item->{'biblioitemnumber'} = $biblioitemnumber;
354 # check for duplicate barcode
355 my %item_errors = CheckItemPreSave($item);
357 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
358 push @bad_item_fields, $item_field;
362 _set_defaults_for_add($item);
363 _set_derived_columns_for_add($item);
364 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
365 warn $error if $error;
366 push @itemnumbers, $itemnumber; # FIXME not checking error
367 $item->{'itemnumber'} = $itemnumber;
369 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
371 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
372 $item_field->replace_with($new_item_marc->field($itemtag));
375 # remove any MARC item fields for rejected items
376 foreach my $item_field (@bad_item_fields) {
377 $record->delete_field($item_field);
380 # update the MARC biblio
381 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
383 return (\@itemnumbers, \@errors);
386 =head2 ModItemFromMarc
390 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
394 This function updates an item record based on a supplied
395 C<MARC::Record> object containing an embedded item field.
396 This API is meant for the use of C<additem.pl>; for
397 other purposes, C<ModItem> should be used.
399 This function uses the hash %default_values_for_mod_from_marc,
400 which contains default values for item fields to
401 apply when modifying an item. This is needed beccause
402 if an item field's value is cleared, TransformMarcToKoha
403 does not include the column in the
404 hash that's passed to ModItem, which without
405 use of this hash makes it impossible to clear
406 an item field's value. See bug 2466.
408 Note that only columns that can be directly
409 changed from the cataloging and serials
410 item editors are included in this hash.
414 my %default_values_for_mod_from_marc = (
416 booksellerid => undef,
418 'items.cn_source' => undef,
421 dateaccessioned => undef,
423 holdingbranch => undef,
425 itemcallnumber => undef,
434 replacementprice => undef,
435 replacementpricedate => undef,
442 sub ModItemFromMarc {
443 my $item_marc = shift;
444 my $biblionumber = shift;
445 my $itemnumber = shift;
447 my $dbh = C4::Context->dbh;
448 my $frameworkcode = GetFrameworkCode( $biblionumber );
449 my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
450 foreach my $item_field (keys %default_values_for_mod_from_marc) {
451 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
453 my $unlinked_item_subfields = _get_unlinked_item_subfields($item_marc, $frameworkcode);
455 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
462 ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]);
466 Change one or more columns in an item record and update
467 the MARC representation of the item.
469 The first argument is a hashref mapping from item column
470 names to the new values. The second and third arguments
471 are the biblionumber and itemnumber, respectively.
473 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
474 an arrayref containing subfields present in the original MARC
475 representation of the item (e.g., from the item editor) that are
476 not mapped to C<items> columns directly but should instead
477 be stored in C<items.more_subfields_xml> and included in
478 the biblio items tag for display and indexing.
480 If one of the changed columns is used to calculate
481 the derived value of a column such as C<items.cn_sort>,
482 this routine will perform the necessary calculation
489 my $biblionumber = shift;
490 my $itemnumber = shift;
492 # if $biblionumber is undefined, get it from the current item
493 unless (defined $biblionumber) {
494 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
497 my $dbh = @_ ? shift : C4::Context->dbh;
498 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
500 my $unlinked_item_subfields;
502 $unlinked_item_subfields = shift;
503 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
506 $item->{'itemnumber'} = $itemnumber or return undef;
507 _set_derived_columns_for_mod($item);
508 _do_column_fixes_for_mod($item);
511 # attempt to change itemnumber
512 # attempt to change biblionumber (if we want
513 # an API to relink an item to a different bib,
514 # it should be a separate function)
517 _koha_modify_item($item);
519 # update biblio MARC XML
520 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
522 unless (defined $unlinked_item_subfields) {
523 $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
525 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields)
526 or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
528 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
529 ($new_item_marc eq '0') and die "$new_item_marc is '0', not hashref"; # logaction line would crash anyway
530 logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
533 =head2 ModItemTransfer
537 ModItemTransfer($itenumber, $frombranch, $tobranch);
541 Marks an item as being transferred from one branch
546 sub ModItemTransfer {
547 my ( $itemnumber, $frombranch, $tobranch ) = @_;
549 my $dbh = C4::Context->dbh;
551 #new entry in branchtransfers....
552 my $sth = $dbh->prepare(
553 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
554 VALUES (?, ?, NOW(), ?)");
555 $sth->execute($itemnumber, $frombranch, $tobranch);
557 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
558 ModDateLastSeen($itemnumber);
562 =head2 ModDateLastSeen
566 ModDateLastSeen($itemnum);
570 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
571 C<$itemnum> is the item number
575 sub ModDateLastSeen {
576 my ($itemnumber) = @_;
578 my $today = C4::Dates->new();
579 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
586 DelItem($biblionumber, $itemnumber);
590 Exported function (core API) for deleting an item record in Koha.
595 my ( $dbh, $biblionumber, $itemnumber ) = @_;
597 # FIXME check the item has no current issues
599 _koha_delete_item( $dbh, $itemnumber );
601 # get the MARC record
602 my $record = GetMarcBiblio($biblionumber);
603 my $frameworkcode = GetFrameworkCode($biblionumber);
606 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
607 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
609 #search item field code
610 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
611 my @fields = $record->field($itemtag);
613 # delete the item specified
614 foreach my $field (@fields) {
615 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
616 $record->delete_field($field);
619 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
620 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
623 =head2 CheckItemPreSave
627 my $item_ref = TransformMarcToKoha($marc, 'items');
629 my %errors = CheckItemPreSave($item_ref);
630 if (exists $errors{'duplicate_barcode'}) {
631 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
632 } elsif (exists $errors{'invalid_homebranch'}) {
633 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
634 } elsif (exists $errors{'invalid_holdingbranch'}) {
635 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
642 Given a hashref containing item fields, determine if it can be
643 inserted or updated in the database. Specifically, checks for
644 database integrity issues, and returns a hash containing any
645 of the following keys, if applicable.
649 =item duplicate_barcode
651 Barcode, if it duplicates one already found in the database.
653 =item invalid_homebranch
655 Home branch, if not defined in branches table.
657 =item invalid_holdingbranch
659 Holding branch, if not defined in branches table.
663 This function does NOT implement any policy-related checks,
664 e.g., whether current operator is allowed to save an
665 item that has a given branch code.
669 sub CheckItemPreSave {
670 my $item_ref = shift;
674 # check for duplicate barcode
675 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
676 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
677 if ($existing_itemnumber) {
678 if (!exists $item_ref->{'itemnumber'} # new item
679 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
680 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
685 # check for valid home branch
686 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
687 my $branch_name = GetBranchName($item_ref->{'homebranch'});
688 unless (defined $branch_name) {
689 # relies on fact that branches.branchname is a non-NULL column,
690 # so GetBranchName returns undef only if branch does not exist
691 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
695 # check for valid holding branch
696 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
697 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
698 unless (defined $branch_name) {
699 # relies on fact that branches.branchname is a non-NULL column,
700 # so GetBranchName returns undef only if branch does not exist
701 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
709 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
711 The following functions provide various ways of
712 getting an item record, a set of item records, or
713 lists of authorized values for certain item fields.
715 Some of the functions in this group are candidates
716 for refactoring -- for example, some of the code
717 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
718 has copy-and-paste work.
726 $itemstatushash = GetItemStatus($fwkcode);
730 Returns a list of valid values for the
731 C<items.notforloan> field.
733 NOTE: does B<not> return an individual item's
736 Can be MARC dependant.
738 But basically could be can be loan or not
739 Create a status selector with the following code
741 =head3 in PERL SCRIPT
745 my $itemstatushash = getitemstatus;
747 foreach my $thisstatus (keys %$itemstatushash) {
748 my %row =(value => $thisstatus,
749 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
751 push @itemstatusloop, \%row;
753 $template->param(statusloop=>\@itemstatusloop);
761 <select name="statusloop">
762 <option value="">Default</option>
763 <!-- TMPL_LOOP name="statusloop" -->
764 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
774 # returns a reference to a hash of references to status...
777 my $dbh = C4::Context->dbh;
779 $fwk = '' unless ($fwk);
780 my ( $tag, $subfield ) =
781 GetMarcFromKohaField( "items.notforloan", $fwk );
782 if ( $tag and $subfield ) {
785 "SELECT authorised_value
786 FROM marc_subfield_structure
792 $sth->execute( $tag, $subfield, $fwk );
793 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
796 "SELECT authorised_value,lib
797 FROM authorised_values
802 $authvalsth->execute($authorisedvaluecat);
803 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
804 $itemstatus{$authorisedvalue} = $lib;
818 $itemstatus{"1"} = "Not For Loan";
822 =head2 GetItemLocation
826 $itemlochash = GetItemLocation($fwk);
830 Returns a list of valid values for the
831 C<items.location> field.
833 NOTE: does B<not> return an individual item's
836 where fwk stands for an optional framework code.
837 Create a location selector with the following code
839 =head3 in PERL SCRIPT
843 my $itemlochash = getitemlocation;
845 foreach my $thisloc (keys %$itemlochash) {
846 my $selected = 1 if $thisbranch eq $branch;
847 my %row =(locval => $thisloc,
848 selected => $selected,
849 locname => $itemlochash->{$thisloc},
851 push @itemlocloop, \%row;
853 $template->param(itemlocationloop => \@itemlocloop);
861 <select name="location">
862 <option value="">Default</option>
863 <!-- TMPL_LOOP name="itemlocationloop" -->
864 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
872 sub GetItemLocation {
874 # returns a reference to a hash of references to location...
877 my $dbh = C4::Context->dbh;
879 $fwk = '' unless ($fwk);
880 my ( $tag, $subfield ) =
881 GetMarcFromKohaField( "items.location", $fwk );
882 if ( $tag and $subfield ) {
885 "SELECT authorised_value
886 FROM marc_subfield_structure
891 $sth->execute( $tag, $subfield, $fwk );
892 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
895 "SELECT authorised_value,lib
896 FROM authorised_values
900 $authvalsth->execute($authorisedvaluecat);
901 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
902 $itemlocation{$authorisedvalue} = $lib;
904 return \%itemlocation;
916 $itemlocation{"1"} = "Not For Loan";
917 return \%itemlocation;
924 $items = GetLostItems( $where, $orderby );
928 This function gets a list of lost items.
934 C<$where> is a hashref. it containts a field of the items table as key
935 and the value to match as value. For example:
937 { barcode => 'abc123',
938 homebranch => 'CPL', }
940 C<$orderby> is a field of the items table by which the resultset
945 C<$items> is a reference to an array full of hashrefs with columns
946 from the "items" table as keys.
948 =item usage in the perl script:
950 my $where = { barcode => '0001548' };
951 my $items = GetLostItems( $where, "homebranch" );
952 $template->param( itemsloop => $items );
959 # Getting input args.
962 my $dbh = C4::Context->dbh;
967 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
968 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
969 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
971 authorised_values.category = 'LOST'
972 AND itemlost IS NOT NULL
975 my @query_parameters;
976 foreach my $key (keys %$where) {
977 $query .= " AND $key LIKE ?";
978 push @query_parameters, "%$where->{$key}%";
980 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
982 if ( defined $orderby && grep($orderby, @ordervalues)) {
983 $query .= ' ORDER BY '.$orderby;
986 my $sth = $dbh->prepare($query);
987 $sth->execute( @query_parameters );
989 while ( my $row = $sth->fetchrow_hashref ){
995 =head2 GetItemsForInventory
999 $itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size, $statushash);
1003 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1005 The sub returns a reference to a list of hashes, each containing
1006 itemnumber, author, title, barcode, item callnumber, and date last
1007 seen. It is ordered by callnumber then title.
1009 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
1010 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1011 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1012 $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.
1016 sub GetItemsForInventory {
1017 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branch, $offset, $size, $statushash ) = @_;
1018 my $dbh = C4::Context->dbh;
1019 my ( @bind_params, @where_strings );
1021 my $query = <<'END_SQL';
1022 SELECT items.itemnumber, barcode, itemcallnumber, title, author, biblio.biblionumber, datelastseen
1024 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
1025 LEFT JOIN biblioitems on items.biblionumber = biblioitems.biblionumber
1028 for my $authvfield (keys %$statushash){
1029 if ( scalar @{$statushash->{$authvfield}} > 0 ){
1030 my $joinedvals = join ',', @{$statushash->{$authvfield}};
1031 push @where_strings, "$authvfield in (" . $joinedvals . ")";
1037 push @where_strings, 'itemcallnumber >= ?';
1038 push @bind_params, $minlocation;
1042 push @where_strings, 'itemcallnumber <= ?';
1043 push @bind_params, $maxlocation;
1046 if ($datelastseen) {
1047 $datelastseen = format_date_in_iso($datelastseen);
1048 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1049 push @bind_params, $datelastseen;
1053 push @where_strings, 'items.location = ?';
1054 push @bind_params, $location;
1058 push @where_strings, 'items.homebranch = ?';
1059 push @bind_params, $branch;
1063 push @where_strings, 'biblioitems.itemtype = ?';
1064 push @bind_params, $itemtype;
1067 if ( $ignoreissued) {
1068 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1069 push @where_strings, 'issues.date_due IS NULL';
1072 if ( @where_strings ) {
1074 $query .= join ' AND ', @where_strings;
1076 $query .= ' ORDER BY itemcallnumber, title';
1077 my $sth = $dbh->prepare($query);
1078 $sth->execute( @bind_params );
1082 while ( my $row = $sth->fetchrow_hashref ) {
1083 $offset-- if ($offset);
1084 $row->{datelastseen}=format_date($row->{datelastseen});
1085 if ( ( !$offset ) && $size ) {
1086 push @results, $row;
1093 =head2 GetItemsCount
1096 $count = &GetItemsCount( $biblionumber);
1100 This function return count of item with $biblionumber
1105 my ( $biblionumber ) = @_;
1106 my $dbh = C4::Context->dbh;
1107 my $query = "SELECT count(*)
1109 WHERE biblionumber=?";
1110 my $sth = $dbh->prepare($query);
1111 $sth->execute($biblionumber);
1112 my $count = $sth->fetchrow;
1116 =head2 GetItemInfosOf
1120 GetItemInfosOf(@itemnumbers);
1126 sub GetItemInfosOf {
1127 my @itemnumbers = @_;
1132 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1134 return get_infos_of( $query, 'itemnumber' );
1137 =head2 GetItemsByBiblioitemnumber
1141 GetItemsByBiblioitemnumber($biblioitemnumber);
1145 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1146 Called by C<C4::XISBN>
1150 sub GetItemsByBiblioitemnumber {
1151 my ( $bibitem ) = @_;
1152 my $dbh = C4::Context->dbh;
1153 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1154 # Get all items attached to a biblioitem
1157 $sth->execute($bibitem) || die $sth->errstr;
1158 while ( my $data = $sth->fetchrow_hashref ) {
1159 # Foreach item, get circulation information
1160 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1161 WHERE itemnumber = ?
1162 AND issues.borrowernumber = borrowers.borrowernumber"
1164 $sth2->execute( $data->{'itemnumber'} );
1165 if ( my $data2 = $sth2->fetchrow_hashref ) {
1166 # if item is out, set the due date and who it is out too
1167 $data->{'date_due'} = $data2->{'date_due'};
1168 $data->{'cardnumber'} = $data2->{'cardnumber'};
1169 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1172 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1173 $data->{'date_due'} = '';
1175 # Find the last 3 people who borrowed this item.
1176 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1177 AND old_issues.borrowernumber = borrowers.borrowernumber
1178 ORDER BY returndate desc,timestamp desc LIMIT 3";
1179 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1180 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1182 while ( my $data2 = $sth2->fetchrow_hashref ) {
1183 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1184 $data->{"card$i2"} = $data2->{'cardnumber'};
1185 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1188 push(@results,$data);
1197 @results = GetItemsInfo($biblionumber, $type);
1201 Returns information about books with the given biblionumber.
1203 C<$type> may be either C<intra> or anything else. If it is not set to
1204 C<intra>, then the search will exclude lost, very overdue, and
1207 C<GetItemsInfo> returns a list of references-to-hash. Each element
1208 contains a number of keys. Most of them are table items from the
1209 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1210 Koha database. Other keys include:
1214 =item C<$data-E<gt>{branchname}>
1216 The name (not the code) of the branch to which the book belongs.
1218 =item C<$data-E<gt>{datelastseen}>
1220 This is simply C<items.datelastseen>, except that while the date is
1221 stored in YYYY-MM-DD format in the database, here it is converted to
1222 DD/MM/YYYY format. A NULL date is returned as C<//>.
1224 =item C<$data-E<gt>{datedue}>
1226 =item C<$data-E<gt>{class}>
1228 This is the concatenation of C<biblioitems.classification>, the book's
1229 Dewey code, and C<biblioitems.subclass>.
1231 =item C<$data-E<gt>{ocount}>
1233 I think this is the number of copies of the book available.
1235 =item C<$data-E<gt>{order}>
1237 If this is set, it is set to C<One Order>.
1244 my ( $biblionumber, $type ) = @_;
1245 my $dbh = C4::Context->dbh;
1246 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1252 biblioitems.itemtype,
1255 biblioitems.publicationyear,
1256 biblioitems.publishercode,
1257 biblioitems.volumedate,
1258 biblioitems.volumedesc,
1261 items.notforloan as itemnotforloan,
1262 itemtypes.description
1264 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1265 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1266 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1267 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1268 $query .= " WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
1269 my $sth = $dbh->prepare($query);
1270 $sth->execute($biblionumber);
1275 my $isth = $dbh->prepare(
1276 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1277 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1278 WHERE itemnumber = ?"
1280 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1281 while ( my $data = $sth->fetchrow_hashref ) {
1284 $isth->execute( $data->{'itemnumber'} );
1285 if ( my $idata = $isth->fetchrow_hashref ) {
1286 $data->{borrowernumber} = $idata->{borrowernumber};
1287 $data->{cardnumber} = $idata->{cardnumber};
1288 $data->{surname} = $idata->{surname};
1289 $data->{firstname} = $idata->{firstname};
1290 $datedue = $idata->{'date_due'};
1291 if (C4::Context->preference("IndependantBranches")){
1292 my $userenv = C4::Context->userenv;
1293 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1294 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1298 if ( $data->{'serial'}) {
1299 $ssth->execute($data->{'itemnumber'}) ;
1300 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1303 if ( $datedue eq '' ) {
1304 my ( $restype, $reserves ) =
1305 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1306 # Previous conditional check with if ($restype) is not needed because a true
1307 # result for one item will result in subsequent items defaulting to this true
1309 $count_reserves = $restype;
1311 #get branch information.....
1312 my $bsth = $dbh->prepare(
1313 "SELECT * FROM branches WHERE branchcode = ?
1316 $bsth->execute( $data->{'holdingbranch'} );
1317 if ( my $bdata = $bsth->fetchrow_hashref ) {
1318 $data->{'branchname'} = $bdata->{'branchname'};
1320 $data->{'datedue'} = $datedue;
1321 $data->{'count_reserves'} = $count_reserves;
1323 # get notforloan complete status if applicable
1324 my $sthnflstatus = $dbh->prepare(
1325 'SELECT authorised_value
1326 FROM marc_subfield_structure
1327 WHERE kohafield="items.notforloan"
1331 $sthnflstatus->execute;
1332 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1333 if ($authorised_valuecode) {
1334 $sthnflstatus = $dbh->prepare(
1335 "SELECT lib FROM authorised_values
1337 AND authorised_value=?"
1339 $sthnflstatus->execute( $authorised_valuecode,
1340 $data->{itemnotforloan} );
1341 my ($lib) = $sthnflstatus->fetchrow;
1342 $data->{notforloanvalue} = $lib;
1344 $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes'));
1346 # my stack procedures
1347 my $stackstatus = $dbh->prepare(
1348 'SELECT authorised_value
1349 FROM marc_subfield_structure
1350 WHERE kohafield="items.stack"
1353 $stackstatus->execute;
1355 ($authorised_valuecode) = $stackstatus->fetchrow;
1356 if ($authorised_valuecode) {
1357 $stackstatus = $dbh->prepare(
1359 FROM authorised_values
1361 AND authorised_value=?
1364 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1365 my ($lib) = $stackstatus->fetchrow;
1366 $data->{stack} = $lib;
1368 # Find the last 3 people who borrowed this item.
1369 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1370 WHERE itemnumber = ?
1371 AND old_issues.borrowernumber = borrowers.borrowernumber
1372 ORDER BY returndate DESC
1374 $sth2->execute($data->{'itemnumber'});
1376 while (my $data2 = $sth2->fetchrow_hashref()) {
1377 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1378 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1379 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1383 $results[$i] = $data;
1387 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1393 =head2 GetLastAcquisitions
1397 my $lastacq = GetLastAcquisitions({'branches' => ('branch1','branch2'), 'itemtypes' => ('BK','BD')}, 10);
1403 sub GetLastAcquisitions {
1404 my ($data,$max) = @_;
1406 my $itemtype = C4::Context->preference('item-level_itypes') ? 'itype' : 'itemtype';
1408 my $number_of_branches = @{$data->{branches}};
1409 my $number_of_itemtypes = @{$data->{itemtypes}};
1412 my @where = ('WHERE 1 ');
1413 $number_of_branches and push @where
1414 , 'AND holdingbranch IN ('
1415 , join(',', ('?') x $number_of_branches )
1419 $number_of_itemtypes and push @where
1420 , "AND $itemtype IN ("
1421 , join(',', ('?') x $number_of_itemtypes )
1425 my $query = "SELECT biblio.biblionumber as biblionumber, title, dateaccessioned
1426 FROM items RIGHT JOIN biblio ON (items.biblionumber=biblio.biblionumber)
1427 RIGHT JOIN biblioitems ON (items.biblioitemnumber=biblioitems.biblioitemnumber)
1429 GROUP BY biblio.biblionumber
1430 ORDER BY dateaccessioned DESC LIMIT $max";
1432 my $dbh = C4::Context->dbh;
1433 my $sth = $dbh->prepare($query);
1435 $sth->execute((@{$data->{branches}}, @{$data->{itemtypes}}));
1438 while( my $row = $sth->fetchrow_hashref){
1439 push @results, {date => $row->{dateaccessioned}
1440 , biblionumber => $row->{biblionumber}
1441 , title => $row->{title}};
1447 =head2 get_itemnumbers_of
1451 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1455 Given a list of biblionumbers, return the list of corresponding itemnumbers
1456 for each biblionumber.
1458 Return a reference on a hash where keys are biblionumbers and values are
1459 references on array of itemnumbers.
1463 sub get_itemnumbers_of {
1464 my @biblionumbers = @_;
1466 my $dbh = C4::Context->dbh;
1472 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1474 my $sth = $dbh->prepare($query);
1475 $sth->execute(@biblionumbers);
1479 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1480 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1483 return \%itemnumbers_of;
1486 =head2 GetItemnumberFromBarcode
1490 $result = GetItemnumberFromBarcode($barcode);
1496 sub GetItemnumberFromBarcode {
1498 my $dbh = C4::Context->dbh;
1501 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1502 $rq->execute($barcode);
1503 my ($result) = $rq->fetchrow;
1507 =head3 get_item_authorised_values
1509 find the types and values for all authorised values assigned to this item.
1514 returns: a hashref malling the authorised value to the value set for this itemnumber
1516 $authorised_values = {
1522 'RESTRICTED' => undef,
1525 'branches' => 'CPL',
1526 'cn_source' => undef,
1527 'itemtypes' => 'SER',
1530 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1534 sub get_item_authorised_values {
1535 my $itemnumber = shift;
1537 # assume that these entries in the authorised_value table are item level.
1538 my $query = q(SELECT distinct authorised_value, kohafield
1539 FROM marc_subfield_structure
1540 WHERE kohafield like 'item%'
1541 AND authorised_value != '' );
1543 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1544 my $iteminfo = GetItem( $itemnumber );
1545 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1547 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1548 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1549 $field =~ s/^items\.//;
1550 if ( exists $iteminfo->{ $field } ) {
1551 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1554 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1558 =head3 get_authorised_value_images
1560 find a list of icons that are appropriate for display based on the
1561 authorised values for a biblio.
1563 parameters: listref of authorised values, such as comes from
1564 get_item_authorised_values or
1565 from C4::Biblio::get_biblio_authorised_values
1567 returns: listref of hashrefs for each image. Each hashref looks like
1570 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1575 Notes: Currently, I put on the full path to the images on the staff
1576 side. This should either be configurable or not done at all. Since I
1577 have to deal with 'intranet' or 'opac' in
1578 get_biblio_authorised_values, perhaps I should be passing it in.
1582 sub get_authorised_value_images {
1583 my $authorised_values = shift;
1587 my $authorised_value_list = GetAuthorisedValues();
1588 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1589 foreach my $this_authorised_value ( @$authorised_value_list ) {
1590 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1591 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1592 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1593 if ( defined $this_authorised_value->{'imageurl'} ) {
1594 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1595 label => $this_authorised_value->{'lib'},
1596 category => $this_authorised_value->{'category'},
1597 value => $this_authorised_value->{'authorised_value'}, };
1602 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1607 =head1 LIMITED USE FUNCTIONS
1609 The following functions, while part of the public API,
1610 are not exported. This is generally because they are
1611 meant to be used by only one script for a specific
1612 purpose, and should not be used in any other context
1613 without careful thought.
1621 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1625 Returns MARC::Record of the item passed in parameter.
1626 This function is meant for use only in C<cataloguing/additem.pl>,
1627 where it is needed to support that script's MARC-like
1633 my ( $biblionumber, $itemnumber ) = @_;
1635 # GetMarcItem has been revised so that it does the following:
1636 # 1. Gets the item information from the items table.
1637 # 2. Converts it to a MARC field for storage in the bib record.
1639 # The previous behavior was:
1640 # 1. Get the bib record.
1641 # 2. Return the MARC tag corresponding to the item record.
1643 # The difference is that one treats the items row as authoritative,
1644 # while the other treats the MARC representation as authoritative
1645 # under certain circumstances.
1647 my $itemrecord = GetItem($itemnumber);
1649 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1650 # Also, don't emit a subfield if the underlying field is blank.
1653 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1654 } keys %{ $itemrecord }
1656 my $itemmarc = TransformKohaToMarc($mungeditem);
1658 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1659 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1660 my @fields = $itemmarc->fields();
1661 if ($#fields > -1) {
1662 $fields[0]->add_subfields(@$unlinked_item_subfields);
1670 =head1 PRIVATE FUNCTIONS AND VARIABLES
1672 The following functions are not meant to be called
1673 directly, but are documented in order to explain
1674 the inner workings of C<C4::Items>.
1678 =head2 %derived_columns
1680 This hash keeps track of item columns that
1681 are strictly derived from other columns in
1682 the item record and are not meant to be set
1685 Each key in the hash should be the name of a
1686 column (as named by TransformMarcToKoha). Each
1687 value should be hashref whose keys are the
1688 columns on which the derived column depends. The
1689 hashref should also contain a 'BUILDER' key
1690 that is a reference to a sub that calculates
1695 my %derived_columns = (
1696 'items.cn_sort' => {
1697 'itemcallnumber' => 1,
1698 'items.cn_source' => 1,
1699 'BUILDER' => \&_calc_items_cn_sort,
1703 =head2 _set_derived_columns_for_add
1707 _set_derived_column_for_add($item);
1711 Given an item hash representing a new item to be added,
1712 calculate any derived columns. Currently the only
1713 such column is C<items.cn_sort>.
1717 sub _set_derived_columns_for_add {
1720 foreach my $column (keys %derived_columns) {
1721 my $builder = $derived_columns{$column}->{'BUILDER'};
1722 my $source_values = {};
1723 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1724 next if $source_column eq 'BUILDER';
1725 $source_values->{$source_column} = $item->{$source_column};
1727 $builder->($item, $source_values);
1731 =head2 _set_derived_columns_for_mod
1735 _set_derived_column_for_mod($item);
1739 Given an item hash representing a new item to be modified.
1740 calculate any derived columns. Currently the only
1741 such column is C<items.cn_sort>.
1743 This routine differs from C<_set_derived_columns_for_add>
1744 in that it needs to handle partial item records. In other
1745 words, the caller of C<ModItem> may have supplied only one
1746 or two columns to be changed, so this function needs to
1747 determine whether any of the columns to be changed affect
1748 any of the derived columns. Also, if a derived column
1749 depends on more than one column, but the caller is not
1750 changing all of then, this routine retrieves the unchanged
1751 values from the database in order to ensure a correct
1756 sub _set_derived_columns_for_mod {
1759 foreach my $column (keys %derived_columns) {
1760 my $builder = $derived_columns{$column}->{'BUILDER'};
1761 my $source_values = {};
1762 my %missing_sources = ();
1763 my $must_recalc = 0;
1764 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1765 next if $source_column eq 'BUILDER';
1766 if (exists $item->{$source_column}) {
1768 $source_values->{$source_column} = $item->{$source_column};
1770 $missing_sources{$source_column} = 1;
1774 foreach my $source_column (keys %missing_sources) {
1775 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1777 $builder->($item, $source_values);
1782 =head2 _do_column_fixes_for_mod
1786 _do_column_fixes_for_mod($item);
1790 Given an item hashref containing one or more
1791 columns to modify, fix up certain values.
1792 Specifically, set to 0 any passed value
1793 of C<notforloan>, C<damaged>, C<itemlost>, or
1794 C<wthdrawn> that is either undefined or
1795 contains the empty string.
1799 sub _do_column_fixes_for_mod {
1802 if (exists $item->{'notforloan'} and
1803 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1804 $item->{'notforloan'} = 0;
1806 if (exists $item->{'damaged'} and
1807 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1808 $item->{'damaged'} = 0;
1810 if (exists $item->{'itemlost'} and
1811 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1812 $item->{'itemlost'} = 0;
1814 if (exists $item->{'wthdrawn'} and
1815 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1816 $item->{'wthdrawn'} = 0;
1818 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1819 $item->{'permanent_location'} = $item->{'location'};
1823 =head2 _get_single_item_column
1827 _get_single_item_column($column, $itemnumber);
1831 Retrieves the value of a single column from an C<items>
1832 row specified by C<$itemnumber>.
1836 sub _get_single_item_column {
1838 my $itemnumber = shift;
1840 my $dbh = C4::Context->dbh;
1841 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1842 $sth->execute($itemnumber);
1843 my ($value) = $sth->fetchrow();
1847 =head2 _calc_items_cn_sort
1851 _calc_items_cn_sort($item, $source_values);
1855 Helper routine to calculate C<items.cn_sort>.
1859 sub _calc_items_cn_sort {
1861 my $source_values = shift;
1863 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1866 =head2 _set_defaults_for_add
1870 _set_defaults_for_add($item_hash);
1874 Given an item hash representing an item to be added, set
1875 correct default values for columns whose default value
1876 is not handled by the DBMS. This includes the following
1883 C<items.dateaccessioned>
1905 sub _set_defaults_for_add {
1907 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1908 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1911 =head2 _koha_new_item
1915 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1919 Perform the actual insert into the C<items> table.
1923 sub _koha_new_item {
1924 my ( $item, $barcode ) = @_;
1925 my $dbh=C4::Context->dbh;
1928 "INSERT INTO items SET
1930 biblioitemnumber = ?,
1932 dateaccessioned = ?,
1936 replacementprice = ?,
1937 replacementpricedate = NOW(),
1938 datelastborrowed = ?,
1939 datelastseen = NOW(),
1962 more_subfields_xml = ?,
1965 my $sth = $dbh->prepare($query);
1967 $item->{'biblionumber'},
1968 $item->{'biblioitemnumber'},
1970 $item->{'dateaccessioned'},
1971 $item->{'booksellerid'},
1972 $item->{'homebranch'},
1974 $item->{'replacementprice'},
1975 $item->{datelastborrowed},
1977 $item->{'notforloan'},
1979 $item->{'itemlost'},
1980 $item->{'wthdrawn'},
1981 $item->{'itemcallnumber'},
1982 $item->{'restricted'},
1983 $item->{'itemnotes'},
1984 $item->{'holdingbranch'},
1986 $item->{'location'},
1989 $item->{'renewals'},
1990 $item->{'reserves'},
1991 $item->{'items.cn_source'},
1992 $item->{'items.cn_sort'},
1995 $item->{'materials'},
1997 $item->{'enumchron'},
1998 $item->{'more_subfields_xml'},
1999 $item->{'copynumber'},
2001 my $itemnumber = $dbh->{'mysql_insertid'};
2002 if ( defined $sth->errstr ) {
2003 $error.="ERROR in _koha_new_item $query".$sth->errstr;
2005 return ( $itemnumber, $error );
2008 =head2 MoveItemFromBiblio
2012 MoveItemFromBiblio($itenumber, $frombiblio, $tobiblio);
2016 Moves an item from a biblio to another
2019 sub MoveItemFromBiblio {
2020 my ($itemnumber, $frombiblio, $tobiblio) = @_;
2021 my $dbh = C4::Context->dbh;
2022 my $sth = $dbh->prepare("UPDATE items SET biblioitemnumber = ?, biblionumber = ? WHERE itemnumber = ? AND biblionumber = ?");
2023 my $return = $sth->execute($tobiblio, $tobiblio, $itemnumber, $frombiblio);
2025 my $record = GetMarcBiblio($frombiblio);
2026 my $frameworkcode = GetFrameworkCode($frombiblio);
2027 ModBiblioMarc($record, $frombiblio, $frameworkcode);
2029 $record = GetMarcBiblio($tobiblio);
2030 $frameworkcode = GetFrameworkCode($frombiblio);
2031 ModBiblioMarc($record, $tobiblio, $frameworkcode);
2041 DelItemCheck($dbh, $biblionumber, $itemnumber);
2045 Exported function (core API) for deleting an item record in Koha if there no current issue.
2050 my ( $dbh, $biblionumber, $itemnumber ) = @_;
2053 # check that there is no issue on this item before deletion.
2054 my $sth=$dbh->prepare("select * from issues i where i.itemnumber=?");
2055 $sth->execute($itemnumber);
2057 my $onloan=$sth->fetchrow;
2060 $error = "book_on_loan"
2062 # check it doesnt have a waiting reserve
2063 $sth=$dbh->prepare("SELECT * FROM reserves WHERE found = 'W' AND itemnumber = ?");
2064 $sth->execute($itemnumber);
2065 my $reserve=$sth->fetchrow;
2067 $error = "book_reserved";
2069 DelItem($dbh, $biblionumber, $itemnumber);
2076 =head2 _koha_modify_item
2080 my ($itemnumber,$error) =_koha_modify_item( $item );
2084 Perform the actual update of the C<items> row. Note that this
2085 routine accepts a hashref specifying the columns to update.
2089 sub _koha_modify_item {
2091 my $dbh=C4::Context->dbh;
2094 my $query = "UPDATE items SET ";
2096 for my $key ( keys %$item ) {
2098 push @bind, $item->{$key};
2101 $query .= " WHERE itemnumber=?";
2102 push @bind, $item->{'itemnumber'};
2103 my $sth = C4::Context->dbh->prepare($query);
2104 $sth->execute(@bind);
2105 if ( C4::Context->dbh->errstr ) {
2106 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
2109 return ($item->{'itemnumber'},$error);
2112 =head2 _koha_delete_item
2116 _koha_delete_item( $dbh, $itemnum );
2120 Internal function to delete an item record from the koha tables
2124 sub _koha_delete_item {
2125 my ( $dbh, $itemnum ) = @_;
2127 # save the deleted item to deleteditems table
2128 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2129 $sth->execute($itemnum);
2130 my $data = $sth->fetchrow_hashref();
2131 my $query = "INSERT INTO deleteditems SET ";
2133 foreach my $key ( keys %$data ) {
2134 $query .= "$key = ?,";
2135 push( @bind, $data->{$key} );
2138 $sth = $dbh->prepare($query);
2139 $sth->execute(@bind);
2141 # delete from items table
2142 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2143 $sth->execute($itemnum);
2147 =head2 _marc_from_item_hash
2151 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2155 Given an item hash representing a complete item record,
2156 create a C<MARC::Record> object containing an embedded
2157 tag representing that item.
2159 The third, optional parameter C<$unlinked_item_subfields> is
2160 an arrayref of subfields (not mapped to C<items> fields per the
2161 framework) to be added to the MARC representation
2166 sub _marc_from_item_hash {
2168 my $frameworkcode = shift;
2169 my $unlinked_item_subfields;
2171 $unlinked_item_subfields = shift;
2174 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2175 # Also, don't emit a subfield if the underlying field is blank.
2176 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2177 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2178 : () } keys %{ $item } };
2180 my $item_marc = MARC::Record->new();
2181 foreach my $item_field (keys %{ $mungeditem }) {
2182 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
2183 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2184 if (my $field = $item_marc->field($tag)) {
2185 $field->add_subfields($subfield => $mungeditem->{$item_field});
2187 my $add_subfields = [];
2188 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2189 $add_subfields = $unlinked_item_subfields;
2191 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field}, @$add_subfields);
2198 =head2 _add_item_field_to_biblio
2202 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2206 Adds the fields from a MARC record containing the
2207 representation of a Koha item record to the MARC
2208 biblio record. The input C<$item_marc> record
2209 is expect to contain just one field, the embedded
2210 item information field.
2214 sub _add_item_field_to_biblio {
2215 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2217 my $biblio_marc = GetMarcBiblio($biblionumber);
2218 foreach my $field ($item_marc->fields()) {
2219 $biblio_marc->append_fields($field);
2222 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2225 =head2 _replace_item_field_in_biblio
2229 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2233 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2234 representation of the item, examine the biblio MARC
2235 for the corresponding tag for that item and
2236 replace it with the tag from C<$item_marc>.
2240 sub _replace_item_field_in_biblio {
2241 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2242 my $dbh = C4::Context->dbh;
2244 # get complete MARC record & replace the item field by the new one
2245 my $completeRecord = GetMarcBiblio($biblionumber);
2246 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2247 my $itemField = $ItemRecord->field($itemtag);
2248 my @items = $completeRecord->field($itemtag);
2251 if ($_->subfield($itemsubfield) eq $itemnumber) {
2252 $_->replace_with($itemField);
2258 # If we haven't found the matching field,
2259 # just add it. However, this means that
2260 # there is likely a bug.
2261 $completeRecord->append_fields($itemField);
2265 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2268 =head2 _repack_item_errors
2270 Add an error message hash generated by C<CheckItemPreSave>
2271 to a list of errors.
2275 sub _repack_item_errors {
2276 my $item_sequence_num = shift;
2277 my $item_ref = shift;
2278 my $error_ref = shift;
2280 my @repacked_errors = ();
2282 foreach my $error_code (sort keys %{ $error_ref }) {
2283 my $repacked_error = {};
2284 $repacked_error->{'item_sequence'} = $item_sequence_num;
2285 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2286 $repacked_error->{'error_code'} = $error_code;
2287 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2288 push @repacked_errors, $repacked_error;
2291 return @repacked_errors;
2294 =head2 _get_unlinked_item_subfields
2298 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2304 sub _get_unlinked_item_subfields {
2305 my $original_item_marc = shift;
2306 my $frameworkcode = shift;
2308 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2310 # assume that this record has only one field, and that that
2311 # field contains only the item information
2313 my @fields = $original_item_marc->fields();
2314 if ($#fields > -1) {
2315 my $field = $fields[0];
2316 my $tag = $field->tag();
2317 foreach my $subfield ($field->subfields()) {
2318 if (defined $subfield->[1] and
2319 $subfield->[1] ne '' and
2320 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2321 push @$subfields, $subfield->[0] => $subfield->[1];
2328 =head2 _get_unlinked_subfields_xml
2332 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2338 sub _get_unlinked_subfields_xml {
2339 my $unlinked_item_subfields = shift;
2342 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2343 my $marc = MARC::Record->new();
2344 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2345 # used in the framework
2346 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2347 $marc->encoding("UTF-8");
2348 $xml = $marc->as_xml("USMARC");
2354 =head2 _parse_unlinked_item_subfields_from_xml
2358 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2364 sub _parse_unlinked_item_subfields_from_xml {
2367 return unless defined $xml and $xml ne "";
2368 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2369 my $unlinked_subfields = [];
2370 my @fields = $marc->fields();
2371 if ($#fields > -1) {
2372 foreach my $subfield ($fields[0]->subfields()) {
2373 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2376 return $unlinked_subfields;