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
73 C4::Items - item management functions
77 This module contains an API for manipulating item
78 records in Koha, and is used by cataloguing, circulation,
79 acquisitions, and serials management.
81 A Koha item record is stored in two places: the
82 items table and embedded in a MARC tag in the XML
83 version of the associated bib record in C<biblioitems.marcxml>.
84 This is done to allow the item information to be readily
85 indexed (e.g., by Zebra), but means that each item
86 modification transaction must keep the items table
87 and the MARC XML in sync at all times.
89 Consequently, all code that creates, modifies, or deletes
90 item records B<must> use an appropriate function from
91 C<C4::Items>. If no existing function is suitable, it is
92 better to add one to C<C4::Items> than to use add
93 one-off SQL statements to add or modify items.
95 The items table will be considered authoritative. In other
96 words, if there is ever a discrepancy between the items
97 table and the MARC XML, the items table should be considered
100 =head1 HISTORICAL NOTE
102 Most of the functions in C<C4::Items> were originally in
103 the C<C4::Biblio> module.
105 =head1 CORE EXPORTED FUNCTIONS
107 The following functions are meant for use by users
116 $item = GetItem($itemnumber,$barcode,$serial);
120 Return item information, for a given itemnumber or barcode.
121 The return value is a hashref mapping item column
122 names to values. If C<$serial> is true, include serial publication data.
127 my ($itemnumber,$barcode, $serial) = @_;
128 my $dbh = C4::Context->dbh;
131 my $sth = $dbh->prepare("
133 WHERE itemnumber = ?");
134 $sth->execute($itemnumber);
135 $data = $sth->fetchrow_hashref;
137 my $sth = $dbh->prepare("
141 $sth->execute($barcode);
142 $data = $sth->fetchrow_hashref;
145 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=?");
146 $ssth->execute($data->{'itemnumber'}) ;
147 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
148 warn $data->{'serialseq'} , $data->{'publisheddate'};
150 #if we don't have an items.itype, use biblioitems.itemtype.
151 if( ! $data->{'itype'} ) {
152 my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
153 $sth->execute($data->{'biblionumber'});
154 ($data->{'itype'}) = $sth->fetchrow_array;
163 CartToShelf($itemnumber);
167 Set the current shelving location of the item record
168 to its stored permanent shelving location. This is
169 primarily used to indicate when an item whose current
170 location is a special processing ('PROC') or shelving cart
171 ('CART') location is back in the stacks.
176 my ( $itemnumber ) = @_;
178 unless ( $itemnumber ) {
179 croak "FAILED CartToShelf() - no itemnumber supplied";
182 my $item = GetItem($itemnumber);
183 $item->{location} = $item->{permanent_location};
184 ModItem($item, undef, $itemnumber);
187 =head2 AddItemFromMarc
191 my ($biblionumber, $biblioitemnumber, $itemnumber)
192 = AddItemFromMarc($source_item_marc, $biblionumber);
196 Given a MARC::Record object containing an embedded item
197 record and a biblionumber, create a new item record.
201 sub AddItemFromMarc {
202 my ( $source_item_marc, $biblionumber ) = @_;
203 my $dbh = C4::Context->dbh;
205 # parse item hash from MARC
206 my $frameworkcode = GetFrameworkCode( $biblionumber );
207 my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
208 my $unlinked_item_subfields = _get_unlinked_item_subfields($source_item_marc, $frameworkcode);
209 return AddItem($item, $biblionumber, $dbh, $frameworkcode, $unlinked_item_subfields);
216 my ($biblionumber, $biblioitemnumber, $itemnumber)
217 = AddItem($item, $biblionumber[, $dbh, $frameworkcode, $unlinked_item_subfields]);
221 Given a hash containing item column names as keys,
222 create a new Koha item record.
224 The first two optional parameters (C<$dbh> and C<$frameworkcode>)
225 do not need to be supplied for general use; they exist
226 simply to allow them to be picked up from AddItemFromMarc.
228 The final optional parameter, C<$unlinked_item_subfields>, contains
229 an arrayref containing subfields present in the original MARC
230 representation of the item (e.g., from the item editor) that are
231 not mapped to C<items> columns directly but should instead
232 be stored in C<items.more_subfields_xml> and included in
233 the biblio items tag for display and indexing.
239 my $biblionumber = shift;
241 my $dbh = @_ ? shift : C4::Context->dbh;
242 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
243 my $unlinked_item_subfields;
245 $unlinked_item_subfields = shift
248 # needs old biblionumber and biblioitemnumber
249 $item->{'biblionumber'} = $biblionumber;
250 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
251 $sth->execute( $item->{'biblionumber'} );
252 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
254 _set_defaults_for_add($item);
255 _set_derived_columns_for_add($item);
256 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
257 # FIXME - checks here
258 unless ( $item->{itype} ) { # default to biblioitem.itemtype if no itype
259 my $itype_sth = $dbh->prepare("SELECT itemtype FROM biblioitems WHERE biblionumber = ?");
260 $itype_sth->execute( $item->{'biblionumber'} );
261 ( $item->{'itype'} ) = $itype_sth->fetchrow_array;
264 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
265 $item->{'itemnumber'} = $itemnumber;
267 # create MARC tag representing item and add to bib
268 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
269 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
271 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
273 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
276 =head2 AddItemBatchFromMarc
280 ($itemnumber_ref, $error_ref) = AddItemBatchFromMarc($record, $biblionumber, $biblioitemnumber, $frameworkcode);
284 Efficiently create item records from a MARC biblio record with
285 embedded item fields. This routine is suitable for batch jobs.
287 This API assumes that the bib record has already been
288 saved to the C<biblio> and C<biblioitems> tables. It does
289 not expect that C<biblioitems.marc> and C<biblioitems.marcxml>
290 are populated, but it will do so via a call to ModBibiloMarc.
292 The goal of this API is to have a similar effect to using AddBiblio
293 and AddItems in succession, but without inefficient repeated
294 parsing of the MARC XML bib record.
296 This function returns an arrayref of new itemsnumbers and an arrayref of item
297 errors encountered during the processing. Each entry in the errors
298 list is a hashref containing the following keys:
304 Sequence number of original item tag in the MARC record.
308 Item barcode, provide to assist in the construction of
309 useful error messages.
311 =item error_condition
313 Code representing the error condition. Can be 'duplicate_barcode',
314 'invalid_homebranch', or 'invalid_holdingbranch'.
316 =item error_information
318 Additional information appropriate to the error condition.
324 sub AddItemBatchFromMarc {
325 my ($record, $biblionumber, $biblioitemnumber, $frameworkcode) = @_;
327 my @itemnumbers = ();
329 my $dbh = C4::Context->dbh;
331 # loop through the item tags and start creating items
332 my @bad_item_fields = ();
333 my ($itemtag, $itemsubfield) = &GetMarcFromKohaField("items.itemnumber",'');
334 my $item_sequence_num = 0;
335 ITEMFIELD: foreach my $item_field ($record->field($itemtag)) {
336 $item_sequence_num++;
337 # we take the item field and stick it into a new
338 # MARC record -- this is required so far because (FIXME)
339 # TransformMarcToKoha requires a MARC::Record, not a MARC::Field
340 # and there is no TransformMarcFieldToKoha
341 my $temp_item_marc = MARC::Record->new();
342 $temp_item_marc->append_fields($item_field);
344 # add biblionumber and biblioitemnumber
345 my $item = TransformMarcToKoha( $dbh, $temp_item_marc, $frameworkcode, 'items' );
346 my $unlinked_item_subfields = _get_unlinked_item_subfields($temp_item_marc, $frameworkcode);
347 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
348 $item->{'biblionumber'} = $biblionumber;
349 $item->{'biblioitemnumber'} = $biblioitemnumber;
351 # check for duplicate barcode
352 my %item_errors = CheckItemPreSave($item);
354 push @errors, _repack_item_errors($item_sequence_num, $item, \%item_errors);
355 push @bad_item_fields, $item_field;
359 _set_defaults_for_add($item);
360 _set_derived_columns_for_add($item);
361 my ( $itemnumber, $error ) = _koha_new_item( $item, $item->{barcode} );
362 warn $error if $error;
363 push @itemnumbers, $itemnumber; # FIXME not checking error
364 $item->{'itemnumber'} = $itemnumber;
366 logaction("CATALOGUING", "ADD", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
368 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode, $unlinked_item_subfields);
369 $item_field->replace_with($new_item_marc->field($itemtag));
372 # remove any MARC item fields for rejected items
373 foreach my $item_field (@bad_item_fields) {
374 $record->delete_field($item_field);
377 # update the MARC biblio
378 $biblionumber = ModBiblioMarc( $record, $biblionumber, $frameworkcode );
380 return (\@itemnumbers, \@errors);
383 =head2 ModItemFromMarc
387 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
391 This function updates an item record based on a supplied
392 C<MARC::Record> object containing an embedded item field.
393 This API is meant for the use of C<additem.pl>; for
394 other purposes, C<ModItem> should be used.
396 This function uses the hash %default_values_for_mod_from_marc,
397 which contains default values for item fields to
398 apply when modifying an item. This is needed beccause
399 if an item field's value is cleared, TransformMarcToKoha
400 does not include the column in the
401 hash that's passed to ModItem, which without
402 use of this hash makes it impossible to clear
403 an item field's value. See bug 2466.
405 Note that only columns that can be directly
406 changed from the cataloging and serials
407 item editors are included in this hash.
411 my %default_values_for_mod_from_marc = (
413 booksellerid => undef,
415 'items.cn_source' => undef,
418 dateaccessioned => undef,
420 holdingbranch => undef,
422 itemcallnumber => undef,
431 replacementprice => undef,
432 replacementpricedate => undef,
439 sub ModItemFromMarc {
440 my $item_marc = shift;
441 my $biblionumber = shift;
442 my $itemnumber = shift;
444 my $dbh = C4::Context->dbh;
445 my $frameworkcode = GetFrameworkCode( $biblionumber );
446 my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
447 foreach my $item_field (keys %default_values_for_mod_from_marc) {
448 $item->{$item_field} = $default_values_for_mod_from_marc{$item_field} unless exists $item->{$item_field};
450 my $unlinked_item_subfields = _get_unlinked_item_subfields($item_marc, $frameworkcode);
452 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode, $unlinked_item_subfields);
459 ModItem({ column => $newvalue }, $biblionumber, $itemnumber[, $original_item_marc]);
463 Change one or more columns in an item record and update
464 the MARC representation of the item.
466 The first argument is a hashref mapping from item column
467 names to the new values. The second and third arguments
468 are the biblionumber and itemnumber, respectively.
470 The fourth, optional parameter, C<$unlinked_item_subfields>, contains
471 an arrayref containing subfields present in the original MARC
472 representation of the item (e.g., from the item editor) that are
473 not mapped to C<items> columns directly but should instead
474 be stored in C<items.more_subfields_xml> and included in
475 the biblio items tag for display and indexing.
477 If one of the changed columns is used to calculate
478 the derived value of a column such as C<items.cn_sort>,
479 this routine will perform the necessary calculation
486 my $biblionumber = shift;
487 my $itemnumber = shift;
489 # if $biblionumber is undefined, get it from the current item
490 unless (defined $biblionumber) {
491 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
494 my $dbh = @_ ? shift : C4::Context->dbh;
495 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
497 my $unlinked_item_subfields;
499 $unlinked_item_subfields = shift;
500 $item->{'more_subfields_xml'} = _get_unlinked_subfields_xml($unlinked_item_subfields);
503 $item->{'itemnumber'} = $itemnumber or return undef;
504 _set_derived_columns_for_mod($item);
505 _do_column_fixes_for_mod($item);
508 # attempt to change itemnumber
509 # attempt to change biblionumber (if we want
510 # an API to relink an item to a different bib,
511 # it should be a separate function)
514 _koha_modify_item($item);
516 # update biblio MARC XML
517 my $whole_item = GetItem($itemnumber) or die "FAILED GetItem($itemnumber)";
519 unless (defined $unlinked_item_subfields) {
520 $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'});
522 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode, $unlinked_item_subfields)
523 or die "FAILED _marc_from_item_hash($whole_item, $frameworkcode)";
525 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
526 ($new_item_marc eq '0') and die "$new_item_marc is '0', not hashref"; # logaction line would crash anyway
527 logaction("CATALOGUING", "MODIFY", $itemnumber, $new_item_marc->as_formatted) if C4::Context->preference("CataloguingLog");
530 =head2 ModItemTransfer
534 ModItemTransfer($itenumber, $frombranch, $tobranch);
538 Marks an item as being transferred from one branch
543 sub ModItemTransfer {
544 my ( $itemnumber, $frombranch, $tobranch ) = @_;
546 my $dbh = C4::Context->dbh;
548 #new entry in branchtransfers....
549 my $sth = $dbh->prepare(
550 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
551 VALUES (?, ?, NOW(), ?)");
552 $sth->execute($itemnumber, $frombranch, $tobranch);
554 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
555 ModDateLastSeen($itemnumber);
559 =head2 ModDateLastSeen
563 ModDateLastSeen($itemnum);
567 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
568 C<$itemnum> is the item number
572 sub ModDateLastSeen {
573 my ($itemnumber) = @_;
575 my $today = C4::Dates->new();
576 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
583 DelItem($biblionumber, $itemnumber);
587 Exported function (core API) for deleting an item record in Koha.
592 my ( $dbh, $biblionumber, $itemnumber ) = @_;
594 # FIXME check the item has no current issues
596 _koha_delete_item( $dbh, $itemnumber );
598 # get the MARC record
599 my $record = GetMarcBiblio($biblionumber);
600 my $frameworkcode = GetFrameworkCode($biblionumber);
603 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
604 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
606 #search item field code
607 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
608 my @fields = $record->field($itemtag);
610 # delete the item specified
611 foreach my $field (@fields) {
612 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
613 $record->delete_field($field);
616 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
617 logaction("CATALOGUING", "DELETE", $itemnumber, "item") if C4::Context->preference("CataloguingLog");
620 =head2 CheckItemPreSave
624 my $item_ref = TransformMarcToKoha($marc, 'items');
626 my %errors = CheckItemPreSave($item_ref);
627 if (exists $errors{'duplicate_barcode'}) {
628 print "item has duplicate barcode: ", $errors{'duplicate_barcode'}, "\n";
629 } elsif (exists $errors{'invalid_homebranch'}) {
630 print "item has invalid home branch: ", $errors{'invalid_homebranch'}, "\n";
631 } elsif (exists $errors{'invalid_holdingbranch'}) {
632 print "item has invalid holding branch: ", $errors{'invalid_holdingbranch'}, "\n";
639 Given a hashref containing item fields, determine if it can be
640 inserted or updated in the database. Specifically, checks for
641 database integrity issues, and returns a hash containing any
642 of the following keys, if applicable.
646 =item duplicate_barcode
648 Barcode, if it duplicates one already found in the database.
650 =item invalid_homebranch
652 Home branch, if not defined in branches table.
654 =item invalid_holdingbranch
656 Holding branch, if not defined in branches table.
660 This function does NOT implement any policy-related checks,
661 e.g., whether current operator is allowed to save an
662 item that has a given branch code.
666 sub CheckItemPreSave {
667 my $item_ref = shift;
671 # check for duplicate barcode
672 if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
673 my $existing_itemnumber = GetItemnumberFromBarcode($item_ref->{'barcode'});
674 if ($existing_itemnumber) {
675 if (!exists $item_ref->{'itemnumber'} # new item
676 or $item_ref->{'itemnumber'} != $existing_itemnumber) { # existing item
677 $errors{'duplicate_barcode'} = $item_ref->{'barcode'};
682 # check for valid home branch
683 if (exists $item_ref->{'homebranch'} and defined $item_ref->{'homebranch'}) {
684 my $branch_name = GetBranchName($item_ref->{'homebranch'});
685 unless (defined $branch_name) {
686 # relies on fact that branches.branchname is a non-NULL column,
687 # so GetBranchName returns undef only if branch does not exist
688 $errors{'invalid_homebranch'} = $item_ref->{'homebranch'};
692 # check for valid holding branch
693 if (exists $item_ref->{'holdingbranch'} and defined $item_ref->{'holdingbranch'}) {
694 my $branch_name = GetBranchName($item_ref->{'holdingbranch'});
695 unless (defined $branch_name) {
696 # relies on fact that branches.branchname is a non-NULL column,
697 # so GetBranchName returns undef only if branch does not exist
698 $errors{'invalid_holdingbranch'} = $item_ref->{'holdingbranch'};
706 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
708 The following functions provide various ways of
709 getting an item record, a set of item records, or
710 lists of authorized values for certain item fields.
712 Some of the functions in this group are candidates
713 for refactoring -- for example, some of the code
714 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
715 has copy-and-paste work.
723 $itemstatushash = GetItemStatus($fwkcode);
727 Returns a list of valid values for the
728 C<items.notforloan> field.
730 NOTE: does B<not> return an individual item's
733 Can be MARC dependant.
735 But basically could be can be loan or not
736 Create a status selector with the following code
738 =head3 in PERL SCRIPT
742 my $itemstatushash = getitemstatus;
744 foreach my $thisstatus (keys %$itemstatushash) {
745 my %row =(value => $thisstatus,
746 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
748 push @itemstatusloop, \%row;
750 $template->param(statusloop=>\@itemstatusloop);
758 <select name="statusloop">
759 <option value="">Default</option>
760 <!-- TMPL_LOOP name="statusloop" -->
761 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
771 # returns a reference to a hash of references to status...
774 my $dbh = C4::Context->dbh;
776 $fwk = '' unless ($fwk);
777 my ( $tag, $subfield ) =
778 GetMarcFromKohaField( "items.notforloan", $fwk );
779 if ( $tag and $subfield ) {
782 "SELECT authorised_value
783 FROM marc_subfield_structure
789 $sth->execute( $tag, $subfield, $fwk );
790 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
793 "SELECT authorised_value,lib
794 FROM authorised_values
799 $authvalsth->execute($authorisedvaluecat);
800 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
801 $itemstatus{$authorisedvalue} = $lib;
817 $itemstatus{"1"} = "Not For Loan";
821 =head2 GetItemLocation
825 $itemlochash = GetItemLocation($fwk);
829 Returns a list of valid values for the
830 C<items.location> field.
832 NOTE: does B<not> return an individual item's
835 where fwk stands for an optional framework code.
836 Create a location selector with the following code
838 =head3 in PERL SCRIPT
842 my $itemlochash = getitemlocation;
844 foreach my $thisloc (keys %$itemlochash) {
845 my $selected = 1 if $thisbranch eq $branch;
846 my %row =(locval => $thisloc,
847 selected => $selected,
848 locname => $itemlochash->{$thisloc},
850 push @itemlocloop, \%row;
852 $template->param(itemlocationloop => \@itemlocloop);
860 <select name="location">
861 <option value="">Default</option>
862 <!-- TMPL_LOOP name="itemlocationloop" -->
863 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
871 sub GetItemLocation {
873 # returns a reference to a hash of references to location...
876 my $dbh = C4::Context->dbh;
878 $fwk = '' unless ($fwk);
879 my ( $tag, $subfield ) =
880 GetMarcFromKohaField( "items.location", $fwk );
881 if ( $tag and $subfield ) {
884 "SELECT authorised_value
885 FROM marc_subfield_structure
890 $sth->execute( $tag, $subfield, $fwk );
891 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
894 "SELECT authorised_value,lib
895 FROM authorised_values
899 $authvalsth->execute($authorisedvaluecat);
900 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
901 $itemlocation{$authorisedvalue} = $lib;
904 return \%itemlocation;
917 $itemlocation{"1"} = "Not For Loan";
918 return \%itemlocation;
925 $items = GetLostItems( $where, $orderby );
929 This function gets a list of lost items.
935 C<$where> is a hashref. it containts a field of the items table as key
936 and the value to match as value. For example:
938 { barcode => 'abc123',
939 homebranch => 'CPL', }
941 C<$orderby> is a field of the items table by which the resultset
946 C<$items> is a reference to an array full of hashrefs with columns
947 from the "items" table as keys.
949 =item usage in the perl script:
951 my $where = { barcode => '0001548' };
952 my $items = GetLostItems( $where, "homebranch" );
953 $template->param( itemsloop => $items );
960 # Getting input args.
963 my $dbh = C4::Context->dbh;
968 LEFT JOIN biblio ON (items.biblionumber = biblio.biblionumber)
969 LEFT JOIN biblioitems ON (items.biblionumber = biblioitems.biblionumber)
970 LEFT JOIN authorised_values ON (items.itemlost = authorised_values.authorised_value)
972 authorised_values.category = 'LOST'
973 AND itemlost IS NOT NULL
976 my @query_parameters;
977 foreach my $key (keys %$where) {
978 $query .= " AND $key LIKE ?";
979 push @query_parameters, "%$where->{$key}%";
981 my @ordervalues = qw/title author homebranch itype barcode price replacementprice lib datelastseen location/;
983 if ( defined $orderby && grep($orderby, @ordervalues)) {
984 $query .= ' ORDER BY '.$orderby;
987 my $sth = $dbh->prepare($query);
988 $sth->execute( @query_parameters );
990 while ( my $row = $sth->fetchrow_hashref ){
996 =head2 GetItemsForInventory
1000 $itemlist = GetItemsForInventory($minlocation, $maxlocation, $location, $itemtype $datelastseen, $branch, $offset, $size);
1004 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
1006 The sub returns a reference to a list of hashes, each containing
1007 itemnumber, author, title, barcode, item callnumber, and date last
1008 seen. It is ordered by callnumber then title.
1010 The required minlocation & maxlocation parameters are used to specify a range of item callnumbers
1011 the datelastseen can be used to specify that you want to see items not seen since a past date only.
1012 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
1016 sub GetItemsForInventory {
1017 my ( $minlocation, $maxlocation,$location, $itemtype, $ignoreissued, $datelastseen, $branch, $offset, $size ) = @_;
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
1029 push @where_strings, 'itemcallnumber >= ?';
1030 push @bind_params, $minlocation;
1034 push @where_strings, 'itemcallnumber <= ?';
1035 push @bind_params, $maxlocation;
1038 if ($datelastseen) {
1039 $datelastseen = format_date_in_iso($datelastseen);
1040 push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)';
1041 push @bind_params, $datelastseen;
1045 push @where_strings, 'items.location = ?';
1046 push @bind_params, $location;
1050 push @where_strings, 'items.homebranch = ?';
1051 push @bind_params, $branch;
1055 push @where_strings, 'biblioitems.itemtype = ?';
1056 push @bind_params, $itemtype;
1059 if ( $ignoreissued) {
1060 $query .= "LEFT JOIN issues ON items.itemnumber = issues.itemnumber ";
1061 push @where_strings, 'issues.date_due IS NULL';
1064 if ( @where_strings ) {
1066 $query .= join ' AND ', @where_strings;
1068 $query .= ' ORDER BY itemcallnumber, title';
1069 my $sth = $dbh->prepare($query);
1070 $sth->execute( @bind_params );
1074 while ( my $row = $sth->fetchrow_hashref ) {
1075 $offset-- if ($offset);
1076 $row->{datelastseen}=format_date($row->{datelastseen});
1077 if ( ( !$offset ) && $size ) {
1078 push @results, $row;
1085 =head2 GetItemsCount
1088 $count = &GetItemsCount( $biblionumber);
1092 This function return count of item with $biblionumber
1097 my ( $biblionumber ) = @_;
1098 my $dbh = C4::Context->dbh;
1099 my $query = "SELECT count(*)
1101 WHERE biblionumber=?";
1102 my $sth = $dbh->prepare($query);
1103 $sth->execute($biblionumber);
1104 my $count = $sth->fetchrow;
1109 =head2 GetItemInfosOf
1113 GetItemInfosOf(@itemnumbers);
1119 sub GetItemInfosOf {
1120 my @itemnumbers = @_;
1125 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
1127 return get_infos_of( $query, 'itemnumber' );
1130 =head2 GetItemsByBiblioitemnumber
1134 GetItemsByBiblioitemnumber($biblioitemnumber);
1138 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
1139 Called by C<C4::XISBN>
1143 sub GetItemsByBiblioitemnumber {
1144 my ( $bibitem ) = @_;
1145 my $dbh = C4::Context->dbh;
1146 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
1147 # Get all items attached to a biblioitem
1150 $sth->execute($bibitem) || die $sth->errstr;
1151 while ( my $data = $sth->fetchrow_hashref ) {
1152 # Foreach item, get circulation information
1153 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
1154 WHERE itemnumber = ?
1155 AND issues.borrowernumber = borrowers.borrowernumber"
1157 $sth2->execute( $data->{'itemnumber'} );
1158 if ( my $data2 = $sth2->fetchrow_hashref ) {
1159 # if item is out, set the due date and who it is out too
1160 $data->{'date_due'} = $data2->{'date_due'};
1161 $data->{'cardnumber'} = $data2->{'cardnumber'};
1162 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
1165 # set date_due to blank, so in the template we check itemlost, and wthdrawn
1166 $data->{'date_due'} = '';
1169 # Find the last 3 people who borrowed this item.
1170 my $query2 = "SELECT * FROM old_issues, borrowers WHERE itemnumber = ?
1171 AND old_issues.borrowernumber = borrowers.borrowernumber
1172 ORDER BY returndate desc,timestamp desc LIMIT 3";
1173 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
1174 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
1176 while ( my $data2 = $sth2->fetchrow_hashref ) {
1177 $data->{"timestamp$i2"} = $data2->{'timestamp'};
1178 $data->{"card$i2"} = $data2->{'cardnumber'};
1179 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
1183 push(@results,$data);
1193 @results = GetItemsInfo($biblionumber, $type);
1197 Returns information about books with the given biblionumber.
1199 C<$type> may be either C<intra> or anything else. If it is not set to
1200 C<intra>, then the search will exclude lost, very overdue, and
1203 C<GetItemsInfo> returns a list of references-to-hash. Each element
1204 contains a number of keys. Most of them are table items from the
1205 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
1206 Koha database. Other keys include:
1210 =item C<$data-E<gt>{branchname}>
1212 The name (not the code) of the branch to which the book belongs.
1214 =item C<$data-E<gt>{datelastseen}>
1216 This is simply C<items.datelastseen>, except that while the date is
1217 stored in YYYY-MM-DD format in the database, here it is converted to
1218 DD/MM/YYYY format. A NULL date is returned as C<//>.
1220 =item C<$data-E<gt>{datedue}>
1222 =item C<$data-E<gt>{class}>
1224 This is the concatenation of C<biblioitems.classification>, the book's
1225 Dewey code, and C<biblioitems.subclass>.
1227 =item C<$data-E<gt>{ocount}>
1229 I think this is the number of copies of the book available.
1231 =item C<$data-E<gt>{order}>
1233 If this is set, it is set to C<One Order>.
1240 my ( $biblionumber, $type ) = @_;
1241 my $dbh = C4::Context->dbh;
1242 # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1248 biblioitems.itemtype,
1251 biblioitems.publicationyear,
1252 biblioitems.publishercode,
1253 biblioitems.volumedate,
1254 biblioitems.volumedesc,
1257 items.notforloan as itemnotforloan,
1258 itemtypes.description
1260 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1261 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1262 LEFT JOIN itemtypes ON itemtypes.itemtype = "
1263 . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1264 $query .= " WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
1265 my $sth = $dbh->prepare($query);
1266 $sth->execute($biblionumber);
1271 my $isth = $dbh->prepare(
1272 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1273 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1274 WHERE itemnumber = ?"
1276 my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? ");
1277 while ( my $data = $sth->fetchrow_hashref ) {
1280 $isth->execute( $data->{'itemnumber'} );
1281 if ( my $idata = $isth->fetchrow_hashref ) {
1282 $data->{borrowernumber} = $idata->{borrowernumber};
1283 $data->{cardnumber} = $idata->{cardnumber};
1284 $data->{surname} = $idata->{surname};
1285 $data->{firstname} = $idata->{firstname};
1286 $datedue = $idata->{'date_due'};
1287 if (C4::Context->preference("IndependantBranches")){
1288 my $userenv = C4::Context->userenv;
1289 if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1290 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1294 if ( $data->{'serial'}) {
1295 $ssth->execute($data->{'itemnumber'}) ;
1296 ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1299 if ( $datedue eq '' ) {
1300 my ( $restype, $reserves ) =
1301 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
1302 # Previous conditional check with if ($restype) is not needed because a true
1303 # result for one item will result in subsequent items defaulting to this true
1305 $count_reserves = $restype;
1309 #get branch information.....
1310 my $bsth = $dbh->prepare(
1311 "SELECT * FROM branches WHERE branchcode = ?
1314 $bsth->execute( $data->{'holdingbranch'} );
1315 if ( my $bdata = $bsth->fetchrow_hashref ) {
1316 $data->{'branchname'} = $bdata->{'branchname'};
1318 $data->{'datedue'} = $datedue;
1319 $data->{'count_reserves'} = $count_reserves;
1321 # get notforloan complete status if applicable
1322 my $sthnflstatus = $dbh->prepare(
1323 'SELECT authorised_value
1324 FROM marc_subfield_structure
1325 WHERE kohafield="items.notforloan"
1329 $sthnflstatus->execute;
1330 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
1331 if ($authorised_valuecode) {
1332 $sthnflstatus = $dbh->prepare(
1333 "SELECT lib FROM authorised_values
1335 AND authorised_value=?"
1337 $sthnflstatus->execute( $authorised_valuecode,
1338 $data->{itemnotforloan} );
1339 my ($lib) = $sthnflstatus->fetchrow;
1340 $data->{notforloanvalue} = $lib;
1342 $data->{itypenotforloan} = $data->{notforloan} if (C4::Context->preference('item-level_itypes'));
1344 # my stack procedures
1345 my $stackstatus = $dbh->prepare(
1346 'SELECT authorised_value
1347 FROM marc_subfield_structure
1348 WHERE kohafield="items.stack"
1351 $stackstatus->execute;
1353 ($authorised_valuecode) = $stackstatus->fetchrow;
1354 if ($authorised_valuecode) {
1355 $stackstatus = $dbh->prepare(
1357 FROM authorised_values
1359 AND authorised_value=?
1362 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
1363 my ($lib) = $stackstatus->fetchrow;
1364 $data->{stack} = $lib;
1366 # Find the last 3 people who borrowed this item.
1367 my $sth2 = $dbh->prepare("SELECT * FROM old_issues,borrowers
1368 WHERE itemnumber = ?
1369 AND old_issues.borrowernumber = borrowers.borrowernumber
1370 ORDER BY returndate DESC
1372 $sth2->execute($data->{'itemnumber'});
1374 while (my $data2 = $sth2->fetchrow_hashref()) {
1375 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
1376 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
1377 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
1381 $results[$i] = $data;
1385 return( sort { ($b->{'publisheddate'} || $b->{'enumchron'}) cmp ($a->{'publisheddate'} || $a->{'enumchron'}) } @results );
1391 =head2 get_itemnumbers_of
1395 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1399 Given a list of biblionumbers, return the list of corresponding itemnumbers
1400 for each biblionumber.
1402 Return a reference on a hash where keys are biblionumbers and values are
1403 references on array of itemnumbers.
1407 sub get_itemnumbers_of {
1408 my @biblionumbers = @_;
1410 my $dbh = C4::Context->dbh;
1416 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1418 my $sth = $dbh->prepare($query);
1419 $sth->execute(@biblionumbers);
1423 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1424 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1427 return \%itemnumbers_of;
1430 =head2 GetItemnumberFromBarcode
1434 $result = GetItemnumberFromBarcode($barcode);
1440 sub GetItemnumberFromBarcode {
1442 my $dbh = C4::Context->dbh;
1445 $dbh->prepare("SELECT itemnumber FROM items WHERE items.barcode=?");
1446 $rq->execute($barcode);
1447 my ($result) = $rq->fetchrow;
1451 =head3 get_item_authorised_values
1453 find the types and values for all authorised values assigned to this item.
1458 returns: a hashref malling the authorised value to the value set for this itemnumber
1460 $authorised_values = {
1466 'RESTRICTED' => undef,
1469 'branches' => 'CPL',
1470 'cn_source' => undef,
1471 'itemtypes' => 'SER',
1474 Notes: see C4::Biblio::get_biblio_authorised_values for a similar method at the biblio level.
1478 sub get_item_authorised_values {
1479 my $itemnumber = shift;
1481 # assume that these entries in the authorised_value table are item level.
1482 my $query = q(SELECT distinct authorised_value, kohafield
1483 FROM marc_subfield_structure
1484 WHERE kohafield like 'item%'
1485 AND authorised_value != '' );
1487 my $itemlevel_authorised_values = C4::Context->dbh->selectall_hashref( $query, 'authorised_value' );
1488 my $iteminfo = GetItem( $itemnumber );
1489 # warn( Data::Dumper->Dump( [ $itemlevel_authorised_values ], [ 'itemlevel_authorised_values' ] ) );
1491 foreach my $this_authorised_value ( keys %$itemlevel_authorised_values ) {
1492 my $field = $itemlevel_authorised_values->{ $this_authorised_value }->{'kohafield'};
1493 $field =~ s/^items\.//;
1494 if ( exists $iteminfo->{ $field } ) {
1495 $return->{ $this_authorised_value } = $iteminfo->{ $field };
1498 # warn( Data::Dumper->Dump( [ $return ], [ 'return' ] ) );
1502 =head3 get_authorised_value_images
1504 find a list of icons that are appropriate for display based on the
1505 authorised values for a biblio.
1507 parameters: listref of authorised values, such as comes from
1508 get_item_authorised_values or
1509 from C4::Biblio::get_biblio_authorised_values
1511 returns: listref of hashrefs for each image. Each hashref looks like
1514 { imageurl => '/intranet-tmpl/prog/img/itemtypeimg/npl/WEB.gif',
1519 Notes: Currently, I put on the full path to the images on the staff
1520 side. This should either be configurable or not done at all. Since I
1521 have to deal with 'intranet' or 'opac' in
1522 get_biblio_authorised_values, perhaps I should be passing it in.
1526 sub get_authorised_value_images {
1527 my $authorised_values = shift;
1531 my $authorised_value_list = GetAuthorisedValues();
1532 # warn ( Data::Dumper->Dump( [ $authorised_value_list ], [ 'authorised_value_list' ] ) );
1533 foreach my $this_authorised_value ( @$authorised_value_list ) {
1534 if ( exists $authorised_values->{ $this_authorised_value->{'category'} }
1535 && $authorised_values->{ $this_authorised_value->{'category'} } eq $this_authorised_value->{'authorised_value'} ) {
1536 # warn ( Data::Dumper->Dump( [ $this_authorised_value ], [ 'this_authorised_value' ] ) );
1537 if ( defined $this_authorised_value->{'imageurl'} ) {
1538 push @imagelist, { imageurl => C4::Koha::getitemtypeimagelocation( 'intranet', $this_authorised_value->{'imageurl'} ),
1539 label => $this_authorised_value->{'lib'},
1540 category => $this_authorised_value->{'category'},
1541 value => $this_authorised_value->{'authorised_value'}, };
1546 # warn ( Data::Dumper->Dump( [ \@imagelist ], [ 'imagelist' ] ) );
1551 =head1 LIMITED USE FUNCTIONS
1553 The following functions, while part of the public API,
1554 are not exported. This is generally because they are
1555 meant to be used by only one script for a specific
1556 purpose, and should not be used in any other context
1557 without careful thought.
1565 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1569 Returns MARC::Record of the item passed in parameter.
1570 This function is meant for use only in C<cataloguing/additem.pl>,
1571 where it is needed to support that script's MARC-like
1577 my ( $biblionumber, $itemnumber ) = @_;
1579 # GetMarcItem has been revised so that it does the following:
1580 # 1. Gets the item information from the items table.
1581 # 2. Converts it to a MARC field for storage in the bib record.
1583 # The previous behavior was:
1584 # 1. Get the bib record.
1585 # 2. Return the MARC tag corresponding to the item record.
1587 # The difference is that one treats the items row as authoritative,
1588 # while the other treats the MARC representation as authoritative
1589 # under certain circumstances.
1591 my $itemrecord = GetItem($itemnumber);
1593 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1594 # Also, don't emit a subfield if the underlying field is blank.
1597 defined($itemrecord->{$_}) && $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : ()
1598 } keys %{ $itemrecord }
1600 my $itemmarc = TransformKohaToMarc($mungeditem);
1602 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($mungeditem->{'items.more_subfields_xml'});
1603 if (defined $unlinked_item_subfields and $#$unlinked_item_subfields > -1) {
1604 my @fields = $itemmarc->fields();
1605 if ($#fields > -1) {
1606 $fields[0]->add_subfields(@$unlinked_item_subfields);
1614 =head1 PRIVATE FUNCTIONS AND VARIABLES
1616 The following functions are not meant to be called
1617 directly, but are documented in order to explain
1618 the inner workings of C<C4::Items>.
1622 =head2 %derived_columns
1624 This hash keeps track of item columns that
1625 are strictly derived from other columns in
1626 the item record and are not meant to be set
1629 Each key in the hash should be the name of a
1630 column (as named by TransformMarcToKoha). Each
1631 value should be hashref whose keys are the
1632 columns on which the derived column depends. The
1633 hashref should also contain a 'BUILDER' key
1634 that is a reference to a sub that calculates
1639 my %derived_columns = (
1640 'items.cn_sort' => {
1641 'itemcallnumber' => 1,
1642 'items.cn_source' => 1,
1643 'BUILDER' => \&_calc_items_cn_sort,
1647 =head2 _set_derived_columns_for_add
1651 _set_derived_column_for_add($item);
1655 Given an item hash representing a new item to be added,
1656 calculate any derived columns. Currently the only
1657 such column is C<items.cn_sort>.
1661 sub _set_derived_columns_for_add {
1664 foreach my $column (keys %derived_columns) {
1665 my $builder = $derived_columns{$column}->{'BUILDER'};
1666 my $source_values = {};
1667 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1668 next if $source_column eq 'BUILDER';
1669 $source_values->{$source_column} = $item->{$source_column};
1671 $builder->($item, $source_values);
1675 =head2 _set_derived_columns_for_mod
1679 _set_derived_column_for_mod($item);
1683 Given an item hash representing a new item to be modified.
1684 calculate any derived columns. Currently the only
1685 such column is C<items.cn_sort>.
1687 This routine differs from C<_set_derived_columns_for_add>
1688 in that it needs to handle partial item records. In other
1689 words, the caller of C<ModItem> may have supplied only one
1690 or two columns to be changed, so this function needs to
1691 determine whether any of the columns to be changed affect
1692 any of the derived columns. Also, if a derived column
1693 depends on more than one column, but the caller is not
1694 changing all of then, this routine retrieves the unchanged
1695 values from the database in order to ensure a correct
1700 sub _set_derived_columns_for_mod {
1703 foreach my $column (keys %derived_columns) {
1704 my $builder = $derived_columns{$column}->{'BUILDER'};
1705 my $source_values = {};
1706 my %missing_sources = ();
1707 my $must_recalc = 0;
1708 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1709 next if $source_column eq 'BUILDER';
1710 if (exists $item->{$source_column}) {
1712 $source_values->{$source_column} = $item->{$source_column};
1714 $missing_sources{$source_column} = 1;
1718 foreach my $source_column (keys %missing_sources) {
1719 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1721 $builder->($item, $source_values);
1726 =head2 _do_column_fixes_for_mod
1730 _do_column_fixes_for_mod($item);
1734 Given an item hashref containing one or more
1735 columns to modify, fix up certain values.
1736 Specifically, set to 0 any passed value
1737 of C<notforloan>, C<damaged>, C<itemlost>, or
1738 C<wthdrawn> that is either undefined or
1739 contains the empty string.
1743 sub _do_column_fixes_for_mod {
1746 if (exists $item->{'notforloan'} and
1747 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1748 $item->{'notforloan'} = 0;
1750 if (exists $item->{'damaged'} and
1751 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1752 $item->{'damaged'} = 0;
1754 if (exists $item->{'itemlost'} and
1755 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1756 $item->{'itemlost'} = 0;
1758 if (exists $item->{'wthdrawn'} and
1759 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1760 $item->{'wthdrawn'} = 0;
1762 if (exists $item->{'location'} && !exists $item->{'permanent_location'}) {
1763 $item->{'permanent_location'} = $item->{'location'};
1767 =head2 _get_single_item_column
1771 _get_single_item_column($column, $itemnumber);
1775 Retrieves the value of a single column from an C<items>
1776 row specified by C<$itemnumber>.
1780 sub _get_single_item_column {
1782 my $itemnumber = shift;
1784 my $dbh = C4::Context->dbh;
1785 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1786 $sth->execute($itemnumber);
1787 my ($value) = $sth->fetchrow();
1791 =head2 _calc_items_cn_sort
1795 _calc_items_cn_sort($item, $source_values);
1799 Helper routine to calculate C<items.cn_sort>.
1803 sub _calc_items_cn_sort {
1805 my $source_values = shift;
1807 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1810 =head2 _set_defaults_for_add
1814 _set_defaults_for_add($item_hash);
1818 Given an item hash representing an item to be added, set
1819 correct default values for columns whose default value
1820 is not handled by the DBMS. This includes the following
1827 C<items.dateaccessioned>
1849 sub _set_defaults_for_add {
1851 $item->{dateaccessioned} ||= C4::Dates->new->output('iso');
1852 $item->{$_} ||= 0 for (qw( notforloan damaged itemlost wthdrawn));
1855 =head2 _koha_new_item
1859 my ($itemnumber,$error) = _koha_new_item( $item, $barcode );
1863 Perform the actual insert into the C<items> table.
1867 sub _koha_new_item {
1868 my ( $item, $barcode ) = @_;
1869 my $dbh=C4::Context->dbh;
1872 "INSERT INTO items SET
1874 biblioitemnumber = ?,
1876 dateaccessioned = ?,
1880 replacementprice = ?,
1881 replacementpricedate = NOW(),
1882 datelastborrowed = ?,
1883 datelastseen = NOW(),
1906 more_subfields_xml = ?,
1909 my $sth = $dbh->prepare($query);
1911 $item->{'biblionumber'},
1912 $item->{'biblioitemnumber'},
1914 $item->{'dateaccessioned'},
1915 $item->{'booksellerid'},
1916 $item->{'homebranch'},
1918 $item->{'replacementprice'},
1919 $item->{datelastborrowed},
1921 $item->{'notforloan'},
1923 $item->{'itemlost'},
1924 $item->{'wthdrawn'},
1925 $item->{'itemcallnumber'},
1926 $item->{'restricted'},
1927 $item->{'itemnotes'},
1928 $item->{'holdingbranch'},
1930 $item->{'location'},
1933 $item->{'renewals'},
1934 $item->{'reserves'},
1935 $item->{'items.cn_source'},
1936 $item->{'items.cn_sort'},
1939 $item->{'materials'},
1941 $item->{'enumchron'},
1942 $item->{'more_subfields_xml'},
1943 $item->{'copynumber'},
1945 my $itemnumber = $dbh->{'mysql_insertid'};
1946 if ( defined $sth->errstr ) {
1947 $error.="ERROR in _koha_new_item $query".$sth->errstr;
1950 return ( $itemnumber, $error );
1953 =head2 _koha_modify_item
1957 my ($itemnumber,$error) =_koha_modify_item( $item );
1961 Perform the actual update of the C<items> row. Note that this
1962 routine accepts a hashref specifying the columns to update.
1966 sub _koha_modify_item {
1968 my $dbh=C4::Context->dbh;
1971 my $query = "UPDATE items SET ";
1973 for my $key ( keys %$item ) {
1975 push @bind, $item->{$key};
1978 $query .= " WHERE itemnumber=?";
1979 push @bind, $item->{'itemnumber'};
1980 my $sth = C4::Context->dbh->prepare($query);
1981 $sth->execute(@bind);
1982 if ( C4::Context->dbh->errstr ) {
1983 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
1987 return ($item->{'itemnumber'},$error);
1990 =head2 _koha_delete_item
1994 _koha_delete_item( $dbh, $itemnum );
1998 Internal function to delete an item record from the koha tables
2002 sub _koha_delete_item {
2003 my ( $dbh, $itemnum ) = @_;
2005 # save the deleted item to deleteditems table
2006 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
2007 $sth->execute($itemnum);
2008 my $data = $sth->fetchrow_hashref();
2010 my $query = "INSERT INTO deleteditems SET ";
2012 foreach my $key ( keys %$data ) {
2013 $query .= "$key = ?,";
2014 push( @bind, $data->{$key} );
2017 $sth = $dbh->prepare($query);
2018 $sth->execute(@bind);
2021 # delete from items table
2022 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
2023 $sth->execute($itemnum);
2028 =head2 _marc_from_item_hash
2032 my $item_marc = _marc_from_item_hash($item, $frameworkcode[, $unlinked_item_subfields]);
2036 Given an item hash representing a complete item record,
2037 create a C<MARC::Record> object containing an embedded
2038 tag representing that item.
2040 The third, optional parameter C<$unlinked_item_subfields> is
2041 an arrayref of subfields (not mapped to C<items> fields per the
2042 framework) to be added to the MARC representation
2047 sub _marc_from_item_hash {
2049 my $frameworkcode = shift;
2050 my $unlinked_item_subfields;
2052 $unlinked_item_subfields = shift;
2055 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
2056 # Also, don't emit a subfield if the underlying field is blank.
2057 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
2058 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
2059 : () } keys %{ $item } };
2061 my $item_marc = MARC::Record->new();
2062 foreach my $item_field (keys %{ $mungeditem }) {
2063 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
2064 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
2065 if (my $field = $item_marc->field($tag)) {
2066 $field->add_subfields($subfield => $mungeditem->{$item_field});
2068 my $add_subfields = [];
2069 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2070 $add_subfields = $unlinked_item_subfields;
2072 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field}, @$add_subfields);
2079 =head2 _add_item_field_to_biblio
2083 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
2087 Adds the fields from a MARC record containing the
2088 representation of a Koha item record to the MARC
2089 biblio record. The input C<$item_marc> record
2090 is expect to contain just one field, the embedded
2091 item information field.
2095 sub _add_item_field_to_biblio {
2096 my ($item_marc, $biblionumber, $frameworkcode) = @_;
2098 my $biblio_marc = GetMarcBiblio($biblionumber);
2099 foreach my $field ($item_marc->fields()) {
2100 $biblio_marc->append_fields($field);
2103 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
2106 =head2 _replace_item_field_in_biblio
2110 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
2114 Given a MARC::Record C<$item_marc> containing one tag with the MARC
2115 representation of the item, examine the biblio MARC
2116 for the corresponding tag for that item and
2117 replace it with the tag from C<$item_marc>.
2121 sub _replace_item_field_in_biblio {
2122 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
2123 my $dbh = C4::Context->dbh;
2125 # get complete MARC record & replace the item field by the new one
2126 my $completeRecord = GetMarcBiblio($biblionumber);
2127 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
2128 my $itemField = $ItemRecord->field($itemtag);
2129 my @items = $completeRecord->field($itemtag);
2132 if ($_->subfield($itemsubfield) eq $itemnumber) {
2133 $_->replace_with($itemField);
2139 # If we haven't found the matching field,
2140 # just add it. However, this means that
2141 # there is likely a bug.
2142 $completeRecord->append_fields($itemField);
2146 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);
2149 =head2 _repack_item_errors
2151 Add an error message hash generated by C<CheckItemPreSave>
2152 to a list of errors.
2156 sub _repack_item_errors {
2157 my $item_sequence_num = shift;
2158 my $item_ref = shift;
2159 my $error_ref = shift;
2161 my @repacked_errors = ();
2163 foreach my $error_code (sort keys %{ $error_ref }) {
2164 my $repacked_error = {};
2165 $repacked_error->{'item_sequence'} = $item_sequence_num;
2166 $repacked_error->{'item_barcode'} = exists($item_ref->{'barcode'}) ? $item_ref->{'barcode'} : '';
2167 $repacked_error->{'error_code'} = $error_code;
2168 $repacked_error->{'error_information'} = $error_ref->{$error_code};
2169 push @repacked_errors, $repacked_error;
2172 return @repacked_errors;
2175 =head2 _get_unlinked_item_subfields
2179 my $unlinked_item_subfields = _get_unlinked_item_subfields($original_item_marc, $frameworkcode);
2185 sub _get_unlinked_item_subfields {
2186 my $original_item_marc = shift;
2187 my $frameworkcode = shift;
2189 my $marcstructure = GetMarcStructure(1, $frameworkcode);
2191 # assume that this record has only one field, and that that
2192 # field contains only the item information
2194 my @fields = $original_item_marc->fields();
2195 if ($#fields > -1) {
2196 my $field = $fields[0];
2197 my $tag = $field->tag();
2198 foreach my $subfield ($field->subfields()) {
2199 if (defined $subfield->[1] and
2200 $subfield->[1] ne '' and
2201 !$marcstructure->{$tag}->{$subfield->[0]}->{'kohafield'}) {
2202 push @$subfields, $subfield->[0] => $subfield->[1];
2209 =head2 _get_unlinked_subfields_xml
2213 my $unlinked_subfields_xml = _get_unlinked_subfields_xml($unlinked_item_subfields);
2219 sub _get_unlinked_subfields_xml {
2220 my $unlinked_item_subfields = shift;
2223 if (defined $unlinked_item_subfields and ref($unlinked_item_subfields) eq 'ARRAY' and $#$unlinked_item_subfields > -1) {
2224 my $marc = MARC::Record->new();
2225 # use of tag 999 is arbitrary, and doesn't need to match the item tag
2226 # used in the framework
2227 $marc->append_fields(MARC::Field->new('999', ' ', ' ', @$unlinked_item_subfields));
2228 $marc->encoding("UTF-8");
2229 $xml = $marc->as_xml("USMARC");
2235 =head2 _parse_unlinked_item_subfields_from_xml
2239 my $unlinked_item_subfields = _parse_unlinked_item_subfields_from_xml($whole_item->{'more_subfields_xml'}):
2245 sub _parse_unlinked_item_subfields_from_xml {
2248 return unless defined $xml and $xml ne "";
2249 my $marc = MARC::Record->new_from_xml(StripNonXmlChars($xml),'UTF-8');
2250 my $unlinked_subfields = [];
2251 my @fields = $marc->fields();
2252 if ($#fields > -1) {
2253 foreach my $subfield ($fields[0]->subfields()) {
2254 push @$unlinked_subfields, $subfield->[0] => $subfield->[1];
2257 return $unlinked_subfields;