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
27 use C4::Dates qw/format_date format_date_in_iso/;
33 use vars qw($VERSION @ISA @EXPORT);
37 @ISA = qw( Exporter );
55 GetItemsByBiblioitemnumber
62 C4::Items - item management functions
66 This module contains an API for manipulating item
67 records in Koha, and is used by cataloguing, circulation,
68 acquisitions, and serials management.
70 A Koha item record is stored in two places: the
71 items table and embedded in a MARC tag in the XML
72 version of the associated bib record in C<biblioitems.marcxml>.
73 This is done to allow the item information to be readily
74 indexed (e.g., by Zebra), but means that each item
75 modification transaction must keep the items table
76 and the MARC XML in sync at all times.
78 Consequently, all code that creates, modifies, or deletes
79 item records B<must> use an appropriate function from
80 C<C4::Items>. If no existing function is suitable, it is
81 better to add one to C<C4::Items> than to use add
82 one-off SQL statements to add or modify items.
84 The items table will be considered authoritative. In other
85 words, if there is ever a discrepancy between the items
86 table and the MARC XML, the items table should be considered
89 =head1 HISTORICAL NOTE
91 Most of the functions in C<C4::Items> were originally in
92 the C<C4::Biblio> module.
94 =head1 CORE EXPORTED FUNCTIONS
96 The following functions are meant for use by users
105 $item = GetItem($itemnumber,$barcode);
109 Return item information, for a given itemnumber or barcode.
110 The return value is a hashref mapping item column
116 my ($itemnumber,$barcode) = @_;
117 my $dbh = C4::Context->dbh;
119 my $sth = $dbh->prepare("
121 WHERE itemnumber = ?");
122 $sth->execute($itemnumber);
123 my $data = $sth->fetchrow_hashref;
126 my $sth = $dbh->prepare("
130 $sth->execute($barcode);
131 my $data = $sth->fetchrow_hashref;
136 =head2 AddItemFromMarc
140 my ($biblionumber, $biblioitemnumber, $itemnumber)
141 = AddItemFromMarc($source_item_marc, $biblionumber);
145 Given a MARC::Record object containing an embedded item
146 record and a biblionumber, create a new item record.
150 sub AddItemFromMarc {
151 my ( $source_item_marc, $biblionumber ) = @_;
152 my $dbh = C4::Context->dbh;
154 # parse item hash from MARC
155 my $frameworkcode = GetFrameworkCode( $biblionumber );
156 my $item = &TransformMarcToKoha( $dbh, $source_item_marc, $frameworkcode );
158 return AddItem($item, $biblionumber, $dbh, $frameworkcode);
165 my ($biblionumber, $biblioitemnumber, $itemnumber)
166 = AddItem($item, $biblionumber[, $dbh, $frameworkcode]);
170 Given a hash containing item column names as keys,
171 create a new Koha item record.
173 The two optional parameters (C<$dbh> and C<$frameworkcode>)
174 do not need to be supplied for general use; they exist
175 simply to allow them to be picked up from AddItemFromMarc.
181 my $biblionumber = shift;
183 my $dbh = @_ ? shift : C4::Context->dbh;
184 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
186 # needs old biblionumber and biblioitemnumber
187 $item->{'biblionumber'} = $biblionumber;
188 my $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
189 $sth->execute( $item->{'biblionumber'} );
190 ($item->{'biblioitemnumber'}) = $sth->fetchrow;
192 _set_defaults_for_add($item);
193 _set_derived_columns_for_add($item);
194 # FIXME - checks here
195 my ( $itemnumber, $error ) = _koha_new_item( $dbh, $item, $item->{barcode} );
196 $item->{'itemnumber'} = $itemnumber;
198 # create MARC tag representing item and add to bib
199 my $new_item_marc = _marc_from_item_hash($item, $frameworkcode);
200 _add_item_field_to_biblio($new_item_marc, $item->{'biblionumber'}, $frameworkcode );
202 logaction(C4::Context->userenv->{'number'},"CATALOGUING","ADD",$itemnumber,"item")
203 if C4::Context->preference("CataloguingLog");
205 return ($item->{biblionumber}, $item->{biblioitemnumber}, $itemnumber);
208 =head2 ModItemFromMarc
212 ModItemFromMarc($item_marc, $biblionumber, $itemnumber);
216 This function updates an item record based on a supplied
217 C<MARC::Record> object containing an embedded item field.
218 This API is meant for the use of C<additem.pl>; for
219 other purposes, C<ModItem> should be used.
223 sub ModItemFromMarc {
224 my $item_marc = shift;
225 my $biblionumber = shift;
226 my $itemnumber = shift;
228 my $dbh = C4::Context->dbh;
229 my $frameworkcode = GetFrameworkCode( $biblionumber );
230 my $item = &TransformMarcToKoha( $dbh, $item_marc, $frameworkcode );
232 return ModItem($item, $biblionumber, $itemnumber, $dbh, $frameworkcode);
239 ModItem({ column => $newvalue }, $biblionumber, $itemnumber);
243 Change one or more columns in an item record and update
244 the MARC representation of the item.
246 The first argument is a hashref mapping from item column
247 names to the new values. The second and third arguments
248 are the biblionumber and itemnumber, respectively.
250 If one of the changed columns is used to calculate
251 the derived value of a column such as C<items.cn_sort>,
252 this routine will perform the necessary calculation
259 my $biblionumber = shift;
260 my $itemnumber = shift;
262 # if $biblionumber is undefined, get it from the current item
263 unless (defined $biblionumber) {
264 $biblionumber = _get_single_item_column('biblionumber', $itemnumber);
267 my $dbh = @_ ? shift : C4::Context->dbh;
268 my $frameworkcode = @_ ? shift : GetFrameworkCode( $biblionumber );
270 $item->{'itemnumber'} = $itemnumber;
271 _set_derived_columns_for_mod($item);
272 _do_column_fixes_for_mod($item);
275 # attempt to change itemnumber
276 # attempt to change biblionumber (if we want
277 # an API to relink an item to a different bib,
278 # it should be a separate function)
281 _koha_modify_item($dbh, $item);
283 # update biblio MARC XML
284 my $whole_item = GetItem($itemnumber);
285 my $new_item_marc = _marc_from_item_hash($whole_item, $frameworkcode);
286 _replace_item_field_in_biblio($new_item_marc, $biblionumber, $itemnumber, $frameworkcode);
288 logaction(C4::Context->userenv->{'number'},"CATALOGUING","MODIFY",$itemnumber,$new_item_marc->as_formatted)
289 if C4::Context->preference("CataloguingLog");
292 =head2 ModItemTransfer
296 ModItemTransfer($itenumber, $frombranch, $tobranch);
300 Marks an item as being transferred from one branch
305 sub ModItemTransfer {
306 my ( $itemnumber, $frombranch, $tobranch ) = @_;
308 my $dbh = C4::Context->dbh;
310 #new entry in branchtransfers....
311 my $sth = $dbh->prepare(
312 "INSERT INTO branchtransfers (itemnumber, frombranch, datesent, tobranch)
313 VALUES (?, ?, NOW(), ?)");
314 $sth->execute($itemnumber, $frombranch, $tobranch);
316 ModItem({ holdingbranch => $tobranch }, undef, $itemnumber);
317 ModDateLastSeen($itemnumber);
321 =head2 ModDateLastSeen
325 ModDateLastSeen($itemnum);
329 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking.
330 C<$itemnum> is the item number
334 sub ModDateLastSeen {
335 my ($itemnumber) = @_;
337 my $today = C4::Dates->new();
338 ModItem({ itemlost => 0, datelastseen => $today->output("iso") }, undef, $itemnumber);
341 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
343 The following functions provide various ways of
344 getting an item record, a set of item records, or
345 lists of authorized values for certain item fields.
347 Some of the functions in this group are candidates
348 for refactoring -- for example, some of the code
349 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
350 has copy-and-paste work.
358 $itemstatushash = GetItemStatus($fwkcode);
362 Returns a list of valid values for the
363 C<items.notforloan> field.
365 NOTE: does B<not> return an individual item's
368 Can be MARC dependant.
370 But basically could be can be loan or not
371 Create a status selector with the following code
373 =head3 in PERL SCRIPT
377 my $itemstatushash = getitemstatus;
379 foreach my $thisstatus (keys %$itemstatushash) {
380 my %row =(value => $thisstatus,
381 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
383 push @itemstatusloop, \%row;
385 $template->param(statusloop=>\@itemstatusloop);
393 <select name="statusloop">
394 <option value="">Default</option>
395 <!-- TMPL_LOOP name="statusloop" -->
396 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
406 # returns a reference to a hash of references to status...
409 my $dbh = C4::Context->dbh;
411 $fwk = '' unless ($fwk);
412 my ( $tag, $subfield ) =
413 GetMarcFromKohaField( "items.notforloan", $fwk );
414 if ( $tag and $subfield ) {
417 "SELECT authorised_value
418 FROM marc_subfield_structure
424 $sth->execute( $tag, $subfield, $fwk );
425 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
428 "SELECT authorised_value,lib
429 FROM authorised_values
434 $authvalsth->execute($authorisedvaluecat);
435 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
436 $itemstatus{$authorisedvalue} = $lib;
452 $itemstatus{"1"} = "Not For Loan";
456 =head2 GetItemLocation
460 $itemlochash = GetItemLocation($fwk);
464 Returns a list of valid values for the
465 C<items.location> field.
467 NOTE: does B<not> return an individual item's
470 where fwk stands for an optional framework code.
471 Create a location selector with the following code
473 =head3 in PERL SCRIPT
477 my $itemlochash = getitemlocation;
479 foreach my $thisloc (keys %$itemlochash) {
480 my $selected = 1 if $thisbranch eq $branch;
481 my %row =(locval => $thisloc,
482 selected => $selected,
483 locname => $itemlochash->{$thisloc},
485 push @itemlocloop, \%row;
487 $template->param(itemlocationloop => \@itemlocloop);
495 <select name="location">
496 <option value="">Default</option>
497 <!-- TMPL_LOOP name="itemlocationloop" -->
498 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
506 sub GetItemLocation {
508 # returns a reference to a hash of references to location...
511 my $dbh = C4::Context->dbh;
513 $fwk = '' unless ($fwk);
514 my ( $tag, $subfield ) =
515 GetMarcFromKohaField( "items.location", $fwk );
516 if ( $tag and $subfield ) {
519 "SELECT authorised_value
520 FROM marc_subfield_structure
525 $sth->execute( $tag, $subfield, $fwk );
526 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
529 "SELECT authorised_value,lib
530 FROM authorised_values
534 $authvalsth->execute($authorisedvaluecat);
535 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
536 $itemlocation{$authorisedvalue} = $lib;
539 return \%itemlocation;
552 $itemlocation{"1"} = "Not For Loan";
553 return \%itemlocation;
560 $items = GetLostItems($where,$orderby);
564 This function get the items lost into C<$items>.
569 C<$where> is a hashref. it containts a field of the items table as key
570 and the value to match as value.
571 C<$orderby> is a field of the items table.
574 C<$items> is a reference to an array full of hasref which keys are items' table column.
576 =item usage in the perl script:
579 $where{barcode} = 0001548;
580 my $items = GetLostItems( \%where, "homebranch" );
581 $template->param(itemsloop => $items);
588 # Getting input args.
591 my $dbh = C4::Context->dbh;
596 WHERE itemlost IS NOT NULL
599 foreach my $key (keys %$where) {
600 $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
602 $query .= " ORDER BY ".$orderby if defined $orderby;
604 my $sth = $dbh->prepare($query);
607 while ( my $row = $sth->fetchrow_hashref ){
613 =head2 GetItemsForInventory
617 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
621 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
623 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
624 It is ordered by callnumber,title.
626 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
627 the datelastseen can be used to specify that you want to see items not seen since a past date only.
628 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
632 sub GetItemsForInventory {
633 my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
634 my $dbh = C4::Context->dbh;
637 $datelastseen=format_date_in_iso($datelastseen);
639 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
641 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
642 WHERE itemcallnumber>= ?
643 AND itemcallnumber <=?
644 AND (datelastseen< ? OR datelastseen IS NULL)";
645 $query.= " AND items.location=".$dbh->quote($location) if $location;
646 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
647 $query .= " ORDER BY itemcallnumber,title";
648 $sth = $dbh->prepare($query);
649 $sth->execute( $minlocation, $maxlocation, $datelastseen );
653 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
655 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
656 WHERE itemcallnumber>= ?
657 AND itemcallnumber <=?";
658 $query.= " AND items.location=".$dbh->quote($location) if $location;
659 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
660 $query .= " ORDER BY itemcallnumber,title";
661 $sth = $dbh->prepare($query);
662 $sth->execute( $minlocation, $maxlocation );
665 while ( my $row = $sth->fetchrow_hashref ) {
666 $offset-- if ($offset);
667 $row->{datelastseen}=format_date($row->{datelastseen});
668 if ( ( !$offset ) && $size ) {
679 $count = &GetItemsCount( $biblionumber);
683 This function return count of item with $biblionumber
688 my ( $biblionumber ) = @_;
689 my $dbh = C4::Context->dbh;
690 my $query = "SELECT count(*)
692 WHERE biblionumber=?";
693 my $sth = $dbh->prepare($query);
694 $sth->execute($biblionumber);
695 my $count = $sth->fetchrow;
700 =head2 GetItemInfosOf
704 GetItemInfosOf(@itemnumbers);
711 my @itemnumbers = @_;
716 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
718 return get_infos_of( $query, 'itemnumber' );
721 =head2 GetItemsByBiblioitemnumber
725 GetItemsByBiblioitemnumber($biblioitemnumber);
729 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
730 Called by C<C4::XISBN>
734 sub GetItemsByBiblioitemnumber {
735 my ( $bibitem ) = @_;
736 my $dbh = C4::Context->dbh;
737 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
738 # Get all items attached to a biblioitem
741 $sth->execute($bibitem) || die $sth->errstr;
742 while ( my $data = $sth->fetchrow_hashref ) {
743 # Foreach item, get circulation information
744 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
746 AND returndate is NULL
747 AND issues.borrowernumber = borrowers.borrowernumber"
749 $sth2->execute( $data->{'itemnumber'} );
750 if ( my $data2 = $sth2->fetchrow_hashref ) {
751 # if item is out, set the due date and who it is out too
752 $data->{'date_due'} = $data2->{'date_due'};
753 $data->{'cardnumber'} = $data2->{'cardnumber'};
754 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
757 # set date_due to blank, so in the template we check itemlost, and wthdrawn
758 $data->{'date_due'} = '';
761 # Find the last 3 people who borrowed this item.
762 my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
763 AND issues.borrowernumber = borrowers.borrowernumber
764 AND returndate is not NULL
765 ORDER BY returndate desc,timestamp desc LIMIT 3";
766 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
767 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
769 while ( my $data2 = $sth2->fetchrow_hashref ) {
770 $data->{"timestamp$i2"} = $data2->{'timestamp'};
771 $data->{"card$i2"} = $data2->{'cardnumber'};
772 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
776 push(@results,$data);
786 @results = GetItemsInfo($biblionumber, $type);
790 Returns information about books with the given biblionumber.
792 C<$type> may be either C<intra> or anything else. If it is not set to
793 C<intra>, then the search will exclude lost, very overdue, and
796 C<GetItemsInfo> returns a list of references-to-hash. Each element
797 contains a number of keys. Most of them are table items from the
798 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
799 Koha database. Other keys include:
803 =item C<$data-E<gt>{branchname}>
805 The name (not the code) of the branch to which the book belongs.
807 =item C<$data-E<gt>{datelastseen}>
809 This is simply C<items.datelastseen>, except that while the date is
810 stored in YYYY-MM-DD format in the database, here it is converted to
811 DD/MM/YYYY format. A NULL date is returned as C<//>.
813 =item C<$data-E<gt>{datedue}>
815 =item C<$data-E<gt>{class}>
817 This is the concatenation of C<biblioitems.classification>, the book's
818 Dewey code, and C<biblioitems.subclass>.
820 =item C<$data-E<gt>{ocount}>
822 I think this is the number of copies of the book available.
824 =item C<$data-E<gt>{order}>
826 If this is set, it is set to C<One Order>.
833 my ( $biblionumber, $type ) = @_;
834 my $dbh = C4::Context->dbh;
835 my $query = "SELECT *,items.notforloan as itemnotforloan
837 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
838 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
839 $query .= (C4::Context->preference('item-level_itypes')) ?
840 " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
841 : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
842 $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
843 my $sth = $dbh->prepare($query);
844 $sth->execute($biblionumber);
847 my ( $date_due, $count_reserves );
849 my $isth = $dbh->prepare(
850 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
851 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
853 AND returndate IS NULL"
855 while ( my $data = $sth->fetchrow_hashref ) {
857 $isth->execute( $data->{'itemnumber'} );
858 if ( my $idata = $isth->fetchrow_hashref ) {
859 $data->{borrowernumber} = $idata->{borrowernumber};
860 $data->{cardnumber} = $idata->{cardnumber};
861 $data->{surname} = $idata->{surname};
862 $data->{firstname} = $idata->{firstname};
863 $datedue = $idata->{'date_due'};
864 if (C4::Context->preference("IndependantBranches")){
865 my $userenv = C4::Context->userenv;
866 if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
867 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
871 if ( $datedue eq '' ) {
872 my ( $restype, $reserves ) =
873 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
875 $count_reserves = $restype;
880 #get branch information.....
881 my $bsth = $dbh->prepare(
882 "SELECT * FROM branches WHERE branchcode = ?
885 $bsth->execute( $data->{'holdingbranch'} );
886 if ( my $bdata = $bsth->fetchrow_hashref ) {
887 $data->{'branchname'} = $bdata->{'branchname'};
889 $data->{'datedue'} = $datedue;
890 $data->{'count_reserves'} = $count_reserves;
892 # get notforloan complete status if applicable
893 my $sthnflstatus = $dbh->prepare(
894 'SELECT authorised_value
895 FROM marc_subfield_structure
896 WHERE kohafield="items.notforloan"
900 $sthnflstatus->execute;
901 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
902 if ($authorised_valuecode) {
903 $sthnflstatus = $dbh->prepare(
904 "SELECT lib FROM authorised_values
906 AND authorised_value=?"
908 $sthnflstatus->execute( $authorised_valuecode,
909 $data->{itemnotforloan} );
910 my ($lib) = $sthnflstatus->fetchrow;
911 $data->{notforloan} = $lib;
914 # my stack procedures
915 my $stackstatus = $dbh->prepare(
916 'SELECT authorised_value
917 FROM marc_subfield_structure
918 WHERE kohafield="items.stack"
921 $stackstatus->execute;
923 ($authorised_valuecode) = $stackstatus->fetchrow;
924 if ($authorised_valuecode) {
925 $stackstatus = $dbh->prepare(
927 FROM authorised_values
929 AND authorised_value=?
932 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
933 my ($lib) = $stackstatus->fetchrow;
934 $data->{stack} = $lib;
936 # Find the last 3 people who borrowed this item.
937 my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
939 AND issues.borrowernumber = borrowers.borrowernumber
940 AND returndate IS NOT NULL LIMIT 3");
941 $sth2->execute($data->{'itemnumber'});
943 while (my $data2 = $sth2->fetchrow_hashref()) {
944 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
945 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
946 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
950 $results[$i] = $data;
958 =head2 get_itemnumbers_of
962 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
966 Given a list of biblionumbers, return the list of corresponding itemnumbers
967 for each biblionumber.
969 Return a reference on a hash where keys are biblionumbers and values are
970 references on array of itemnumbers.
974 sub get_itemnumbers_of {
975 my @biblionumbers = @_;
977 my $dbh = C4::Context->dbh;
983 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
985 my $sth = $dbh->prepare($query);
986 $sth->execute(@biblionumbers);
990 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
991 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
994 return \%itemnumbers_of;
997 =head1 LIMITED USE FUNCTIONS
999 The following functions, while part of the public API,
1000 are not exported. This is generally because they are
1001 meant to be used by only one script for a specific
1002 purpose, and should not be used in any other context
1003 without careful thought.
1011 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1015 Returns MARC::Record of the item passed in parameter.
1016 This function is meant for use only in C<cataloguing/additem.pl>,
1017 where it is needed to support that script's MARC-like
1023 my ( $biblionumber, $itemnumber ) = @_;
1025 # GetMarcItem has been revised so that it does the following:
1026 # 1. Gets the item information from the items table.
1027 # 2. Converts it to a MARC field for storage in the bib record.
1029 # The previous behavior was:
1030 # 1. Get the bib record.
1031 # 2. Return the MARC tag corresponding to the item record.
1033 # The difference is that one treats the items row as authoritative,
1034 # while the other treats the MARC representation as authoritative
1035 # under certain circumstances.
1037 my $itemrecord = GetItem($itemnumber);
1039 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1040 # Also, don't emit a subfield if the underlying field is blank.
1041 my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
1043 my $itemmarc = TransformKohaToMarc($mungeditem);
1048 =head1 PRIVATE FUNCTIONS AND VARIABLES
1050 The following functions are not meant to be called
1051 directly, but are documented in order to explain
1052 the inner workings of C<C4::Items>.
1056 =head2 %derived_columns
1058 This hash keeps track of item columns that
1059 are strictly derived from other columns in
1060 the item record and are not meant to be set
1063 Each key in the hash should be the name of a
1064 column (as named by TransformMarcToKoha). Each
1065 value should be hashref whose keys are the
1066 columns on which the derived column depends. The
1067 hashref should also contain a 'BUILDER' key
1068 that is a reference to a sub that calculates
1073 my %derived_columns = (
1074 'items.cn_sort' => {
1075 'itemcallnumber' => 1,
1076 'items.cn_source' => 1,
1077 'BUILDER' => \&_calc_items_cn_sort,
1081 =head2 _set_derived_columns_for_add
1085 _set_derived_column_for_add($item);
1089 Given an item hash representing a new item to be added,
1090 calculate any derived columns. Currently the only
1091 such column is C<items.cn_sort>.
1095 sub _set_derived_columns_for_add {
1098 foreach my $column (keys %derived_columns) {
1099 my $builder = $derived_columns{$column}->{'BUILDER'};
1100 my $source_values = {};
1101 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1102 next if $source_column eq 'BUILDER';
1103 $source_values->{$source_column} = $item->{$source_column};
1105 $builder->($item, $source_values);
1109 =head2 _set_derived_columns_for_mod
1113 _set_derived_column_for_mod($item);
1117 Given an item hash representing a new item to be modified.
1118 calculate any derived columns. Currently the only
1119 such column is C<items.cn_sort>.
1121 This routine differs from C<_set_derived_columns_for_add>
1122 in that it needs to handle partial item records. In other
1123 words, the caller of C<ModItem> may have supplied only one
1124 or two columns to be changed, so this function needs to
1125 determine whether any of the columns to be changed affect
1126 any of the derived columns. Also, if a derived column
1127 depends on more than one column, but the caller is not
1128 changing all of then, this routine retrieves the unchanged
1129 values from the database in order to ensure a correct
1134 sub _set_derived_columns_for_mod {
1137 foreach my $column (keys %derived_columns) {
1138 my $builder = $derived_columns{$column}->{'BUILDER'};
1139 my $source_values = {};
1140 my %missing_sources = ();
1141 my $must_recalc = 0;
1142 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1143 next if $source_column eq 'BUILDER';
1144 if (exists $item->{$source_column}) {
1146 $source_values->{$source_column} = $item->{$source_column};
1148 $missing_sources{$source_column} = 1;
1152 foreach my $source_column (keys %missing_sources) {
1153 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1155 $builder->($item, $source_values);
1160 =head2 _do_column_fixes_for_mod
1164 _do_column_fixes_for_mod($item);
1168 Given an item hashref containing one or more
1169 columns to modify, fix up certain values.
1170 Specifically, set to 0 any passed value
1171 of C<notforloan>, C<damaged>, C<itemlost>, or
1172 C<wthdrawn> that is either undefined or
1173 contains the empty string.
1177 sub _do_column_fixes_for_mod {
1180 if (exists $item->{'notforloan'} and
1181 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1182 $item->{'notforloan'} = 0;
1184 if (exists $item->{'damaged'} and
1185 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1186 $item->{'damaged'} = 0;
1188 if (exists $item->{'itemlost'} and
1189 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1190 $item->{'itemlost'} = 0;
1192 if (exists $item->{'wthdrawn'} and
1193 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1194 $item->{'wthdrawn'} = 0;
1198 =head2 _get_single_item_column
1202 _get_single_item_column($column, $itemnumber);
1206 Retrieves the value of a single column from an C<items>
1207 row specified by C<$itemnumber>.
1211 sub _get_single_item_column {
1213 my $itemnumber = shift;
1215 my $dbh = C4::Context->dbh;
1216 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1217 $sth->execute($itemnumber);
1218 my ($value) = $sth->fetchrow();
1222 =head2 _calc_items_cn_sort
1226 _calc_items_cn_sort($item, $source_values);
1230 Helper routine to calculate C<items.cn_sort>.
1234 sub _calc_items_cn_sort {
1236 my $source_values = shift;
1238 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1241 =head2 _set_defaults_for_add
1245 _set_defaults_for_add($item_hash);
1249 Given an item hash representing an item to be added, set
1250 correct default values for columns whose default value
1251 is not handled by the DBMS. This includes the following
1258 C<items.dateaccessioned>
1280 sub _set_defaults_for_add {
1283 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
1284 if (!(exists $item->{'dateaccessioned'}) ||
1285 ($item->{'dateaccessioned'} eq '')) {
1286 # FIXME add check for invalid date
1287 my $today = C4::Dates->new();
1288 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
1291 # various item status fields cannot be null
1292 $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
1293 $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'};
1294 $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'};
1295 $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'};
1298 =head2 _koha_new_item
1302 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
1306 Perform the actual insert into the C<items> table.
1310 sub _koha_new_item {
1311 my ( $dbh, $item, $barcode ) = @_;
1315 "INSERT INTO items SET
1317 biblioitemnumber = ?,
1319 dateaccessioned = ?,
1323 replacementprice = ?,
1324 replacementpricedate = NOW(),
1325 datelastborrowed = ?,
1326 datelastseen = NOW(),
1349 my $sth = $dbh->prepare($query);
1351 $item->{'biblionumber'},
1352 $item->{'biblioitemnumber'},
1354 $item->{'dateaccessioned'},
1355 $item->{'booksellerid'},
1356 $item->{'homebranch'},
1358 $item->{'replacementprice'},
1359 $item->{datelastborrowed},
1361 $item->{'notforloan'},
1363 $item->{'itemlost'},
1364 $item->{'wthdrawn'},
1365 $item->{'itemcallnumber'},
1366 $item->{'restricted'},
1367 $item->{'itemnotes'},
1368 $item->{'holdingbranch'},
1370 $item->{'location'},
1373 $item->{'renewals'},
1374 $item->{'reserves'},
1375 $item->{'items.cn_source'},
1376 $item->{'items.cn_sort'},
1379 $item->{'materials'},
1382 my $itemnumber = $dbh->{'mysql_insertid'};
1383 if ( defined $sth->errstr ) {
1384 $error.="ERROR in _koha_new_item $query".$sth->errstr;
1387 return ( $itemnumber, $error );
1390 =head2 _koha_modify_item
1394 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
1398 Perform the actual update of the C<items> row. Note that this
1399 routine accepts a hashref specifying the columns to update.
1403 sub _koha_modify_item {
1404 my ( $dbh, $item ) = @_;
1407 my $query = "UPDATE items SET ";
1409 for my $key ( keys %$item ) {
1411 push @bind, $item->{$key};
1414 $query .= " WHERE itemnumber=?";
1415 push @bind, $item->{'itemnumber'};
1416 my $sth = $dbh->prepare($query);
1417 $sth->execute(@bind);
1418 if ( $dbh->errstr ) {
1419 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
1423 return ($item->{'itemnumber'},$error);
1426 =head2 _marc_from_item_hash
1430 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
1434 Given an item hash representing a complete item record,
1435 create a C<MARC::Record> object containing an embedded
1436 tag representing that item.
1440 sub _marc_from_item_hash {
1442 my $frameworkcode = shift;
1444 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
1445 # Also, don't emit a subfield if the underlying field is blank.
1446 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
1447 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
1448 : () } keys %{ $item } };
1450 my $item_marc = MARC::Record->new();
1451 foreach my $item_field (keys %{ $mungeditem }) {
1452 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
1453 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
1454 if (my $field = $item_marc->field($tag)) {
1455 $field->add_subfields($subfield => $mungeditem->{$item_field});
1457 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field});
1464 =head2 _add_item_field_to_biblio
1468 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
1472 Adds the fields from a MARC record containing the
1473 representation of a Koha item record to the MARC
1474 biblio record. The input C<$item_marc> record
1475 is expect to contain just one field, the embedded
1476 item information field.
1480 sub _add_item_field_to_biblio {
1481 my ($item_marc, $biblionumber, $frameworkcode) = @_;
1483 my $biblio_marc = GetMarcBiblio($biblionumber);
1485 foreach my $field ($item_marc->fields()) {
1486 $biblio_marc->append_fields($field);
1489 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
1492 =head2 _replace_item_field_in_biblio
1496 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
1500 Given a MARC::Record C<$item_marc> containing one tag with the MARC
1501 representation of the item, examine the biblio MARC
1502 for the corresponding tag for that item and
1503 replace it with the tag from C<$item_marc>.
1507 sub _replace_item_field_in_biblio {
1508 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
1509 my $dbh = C4::Context->dbh;
1511 # get complete MARC record & replace the item field by the new one
1512 my $completeRecord = GetMarcBiblio($biblionumber);
1513 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
1514 my $itemField = $ItemRecord->field($itemtag);
1515 my @items = $completeRecord->field($itemtag);
1518 if ($_->subfield($itemsubfield) eq $itemnumber) {
1519 $_->replace_with($itemField);
1525 # If we haven't found the matching field,
1526 # just add it. However, this means that
1527 # there is likely a bug.
1528 $completeRecord->append_fields($itemField);
1532 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);