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/;
32 use vars qw($VERSION @ISA @EXPORT);
36 @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);
345 DelItem($biblionumber, $itemnumber);
349 Exported function (core API) for deleting an item record in Koha.
354 my ( $dbh, $biblionumber, $itemnumber ) = @_;
356 # FIXME check the item has no current issues
358 _koha_delete_item( $dbh, $itemnumber );
360 # get the MARC record
361 my $record = GetMarcBiblio($biblionumber);
362 my $frameworkcode = GetFrameworkCode($biblionumber);
365 my $copy2deleted = $dbh->prepare("UPDATE deleteditems SET marc=? WHERE itemnumber=?");
366 $copy2deleted->execute( $record->as_usmarc(), $itemnumber );
368 #search item field code
369 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
370 my @fields = $record->field($itemtag);
372 # delete the item specified
373 foreach my $field (@fields) {
374 if ( $field->subfield($itemsubfield) eq $itemnumber ) {
375 $record->delete_field($field);
378 &ModBiblioMarc( $record, $biblionumber, $frameworkcode );
379 &logaction(C4::Context->userenv->{'number'},"CATALOGUING","DELETE",$itemnumber,"item")
380 if C4::Context->preference("CataloguingLog");
383 =head1 EXPORTED SPECIAL ACCESSOR FUNCTIONS
385 The following functions provide various ways of
386 getting an item record, a set of item records, or
387 lists of authorized values for certain item fields.
389 Some of the functions in this group are candidates
390 for refactoring -- for example, some of the code
391 in C<GetItemsByBiblioitemnumber> and C<GetItemsInfo>
392 has copy-and-paste work.
400 $itemstatushash = GetItemStatus($fwkcode);
404 Returns a list of valid values for the
405 C<items.notforloan> field.
407 NOTE: does B<not> return an individual item's
410 Can be MARC dependant.
412 But basically could be can be loan or not
413 Create a status selector with the following code
415 =head3 in PERL SCRIPT
419 my $itemstatushash = getitemstatus;
421 foreach my $thisstatus (keys %$itemstatushash) {
422 my %row =(value => $thisstatus,
423 statusname => $itemstatushash->{$thisstatus}->{'statusname'},
425 push @itemstatusloop, \%row;
427 $template->param(statusloop=>\@itemstatusloop);
435 <select name="statusloop">
436 <option value="">Default</option>
437 <!-- TMPL_LOOP name="statusloop" -->
438 <option value="<!-- TMPL_VAR name="value" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="statusname" --></option>
448 # returns a reference to a hash of references to status...
451 my $dbh = C4::Context->dbh;
453 $fwk = '' unless ($fwk);
454 my ( $tag, $subfield ) =
455 GetMarcFromKohaField( "items.notforloan", $fwk );
456 if ( $tag and $subfield ) {
459 "SELECT authorised_value
460 FROM marc_subfield_structure
466 $sth->execute( $tag, $subfield, $fwk );
467 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
470 "SELECT authorised_value,lib
471 FROM authorised_values
476 $authvalsth->execute($authorisedvaluecat);
477 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
478 $itemstatus{$authorisedvalue} = $lib;
494 $itemstatus{"1"} = "Not For Loan";
498 =head2 GetItemLocation
502 $itemlochash = GetItemLocation($fwk);
506 Returns a list of valid values for the
507 C<items.location> field.
509 NOTE: does B<not> return an individual item's
512 where fwk stands for an optional framework code.
513 Create a location selector with the following code
515 =head3 in PERL SCRIPT
519 my $itemlochash = getitemlocation;
521 foreach my $thisloc (keys %$itemlochash) {
522 my $selected = 1 if $thisbranch eq $branch;
523 my %row =(locval => $thisloc,
524 selected => $selected,
525 locname => $itemlochash->{$thisloc},
527 push @itemlocloop, \%row;
529 $template->param(itemlocationloop => \@itemlocloop);
537 <select name="location">
538 <option value="">Default</option>
539 <!-- TMPL_LOOP name="itemlocationloop" -->
540 <option value="<!-- TMPL_VAR name="locval" -->" <!-- TMPL_IF name="selected" -->selected<!-- /TMPL_IF -->><!-- TMPL_VAR name="locname" --></option>
548 sub GetItemLocation {
550 # returns a reference to a hash of references to location...
553 my $dbh = C4::Context->dbh;
555 $fwk = '' unless ($fwk);
556 my ( $tag, $subfield ) =
557 GetMarcFromKohaField( "items.location", $fwk );
558 if ( $tag and $subfield ) {
561 "SELECT authorised_value
562 FROM marc_subfield_structure
567 $sth->execute( $tag, $subfield, $fwk );
568 if ( my ($authorisedvaluecat) = $sth->fetchrow ) {
571 "SELECT authorised_value,lib
572 FROM authorised_values
576 $authvalsth->execute($authorisedvaluecat);
577 while ( my ( $authorisedvalue, $lib ) = $authvalsth->fetchrow ) {
578 $itemlocation{$authorisedvalue} = $lib;
581 return \%itemlocation;
594 $itemlocation{"1"} = "Not For Loan";
595 return \%itemlocation;
602 $items = GetLostItems($where,$orderby);
606 This function get the items lost into C<$items>.
611 C<$where> is a hashref. it containts a field of the items table as key
612 and the value to match as value.
613 C<$orderby> is a field of the items table.
616 C<$items> is a reference to an array full of hasref which keys are items' table column.
618 =item usage in the perl script:
621 $where{barcode} = 0001548;
622 my $items = GetLostItems( \%where, "homebranch" );
623 $template->param(itemsloop => $items);
630 # Getting input args.
633 my $dbh = C4::Context->dbh;
638 WHERE itemlost IS NOT NULL
641 foreach my $key (keys %$where) {
642 $query .= " AND " . $key . " LIKE '%" . $where->{$key} . "%'";
644 $query .= " ORDER BY ".$orderby if defined $orderby;
646 my $sth = $dbh->prepare($query);
649 while ( my $row = $sth->fetchrow_hashref ){
655 =head2 GetItemsForInventory
659 $itemlist = GetItemsForInventory($minlocation,$maxlocation,$datelastseen,$offset,$size)
663 Retrieve a list of title/authors/barcode/callnumber, for biblio inventory.
665 The sub returns a list of hashes, containing itemnumber, author, title, barcode & item callnumber.
666 It is ordered by callnumber,title.
668 The minlocation & maxlocation parameters are used to specify a range of item callnumbers
669 the datelastseen can be used to specify that you want to see items not seen since a past date only.
670 offset & size can be used to retrieve only a part of the whole listing (defaut behaviour)
674 sub GetItemsForInventory {
675 my ( $minlocation, $maxlocation,$location, $datelastseen, $branch, $offset, $size ) = @_;
676 my $dbh = C4::Context->dbh;
679 $datelastseen=format_date_in_iso($datelastseen);
681 "SELECT itemnumber,barcode,itemcallnumber,title,author,biblio.biblionumber,datelastseen
683 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
684 WHERE itemcallnumber>= ?
685 AND itemcallnumber <=?
686 AND (datelastseen< ? OR datelastseen IS NULL)";
687 $query.= " AND items.location=".$dbh->quote($location) if $location;
688 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
689 $query .= " ORDER BY itemcallnumber,title";
690 $sth = $dbh->prepare($query);
691 $sth->execute( $minlocation, $maxlocation, $datelastseen );
695 SELECT itemnumber,barcode,itemcallnumber,biblio.biblionumber,title,author,datelastseen
697 LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
698 WHERE itemcallnumber>= ?
699 AND itemcallnumber <=?";
700 $query.= " AND items.location=".$dbh->quote($location) if $location;
701 $query.= " AND items.homebranch=".$dbh->quote($branch) if $branch;
702 $query .= " ORDER BY itemcallnumber,title";
703 $sth = $dbh->prepare($query);
704 $sth->execute( $minlocation, $maxlocation );
707 while ( my $row = $sth->fetchrow_hashref ) {
708 $offset-- if ($offset);
709 $row->{datelastseen}=format_date($row->{datelastseen});
710 if ( ( !$offset ) && $size ) {
721 $count = &GetItemsCount( $biblionumber);
725 This function return count of item with $biblionumber
730 my ( $biblionumber ) = @_;
731 my $dbh = C4::Context->dbh;
732 my $query = "SELECT count(*)
734 WHERE biblionumber=?";
735 my $sth = $dbh->prepare($query);
736 $sth->execute($biblionumber);
737 my $count = $sth->fetchrow;
742 =head2 GetItemInfosOf
746 GetItemInfosOf(@itemnumbers);
753 my @itemnumbers = @_;
758 WHERE itemnumber IN (' . join( ',', @itemnumbers ) . ')
760 return get_infos_of( $query, 'itemnumber' );
763 =head2 GetItemsByBiblioitemnumber
767 GetItemsByBiblioitemnumber($biblioitemnumber);
771 Returns an arrayref of hashrefs suitable for use in a TMPL_LOOP
772 Called by C<C4::XISBN>
776 sub GetItemsByBiblioitemnumber {
777 my ( $bibitem ) = @_;
778 my $dbh = C4::Context->dbh;
779 my $sth = $dbh->prepare("SELECT * FROM items WHERE items.biblioitemnumber = ?") || die $dbh->errstr;
780 # Get all items attached to a biblioitem
783 $sth->execute($bibitem) || die $sth->errstr;
784 while ( my $data = $sth->fetchrow_hashref ) {
785 # Foreach item, get circulation information
786 my $sth2 = $dbh->prepare( "SELECT * FROM issues,borrowers
788 AND returndate is NULL
789 AND issues.borrowernumber = borrowers.borrowernumber"
791 $sth2->execute( $data->{'itemnumber'} );
792 if ( my $data2 = $sth2->fetchrow_hashref ) {
793 # if item is out, set the due date and who it is out too
794 $data->{'date_due'} = $data2->{'date_due'};
795 $data->{'cardnumber'} = $data2->{'cardnumber'};
796 $data->{'borrowernumber'} = $data2->{'borrowernumber'};
799 # set date_due to blank, so in the template we check itemlost, and wthdrawn
800 $data->{'date_due'} = '';
803 # Find the last 3 people who borrowed this item.
804 my $query2 = "SELECT * FROM issues, borrowers WHERE itemnumber = ?
805 AND issues.borrowernumber = borrowers.borrowernumber
806 AND returndate is not NULL
807 ORDER BY returndate desc,timestamp desc LIMIT 3";
808 $sth2 = $dbh->prepare($query2) || die $dbh->errstr;
809 $sth2->execute( $data->{'itemnumber'} ) || die $sth2->errstr;
811 while ( my $data2 = $sth2->fetchrow_hashref ) {
812 $data->{"timestamp$i2"} = $data2->{'timestamp'};
813 $data->{"card$i2"} = $data2->{'cardnumber'};
814 $data->{"borrower$i2"} = $data2->{'borrowernumber'};
818 push(@results,$data);
828 @results = GetItemsInfo($biblionumber, $type);
832 Returns information about books with the given biblionumber.
834 C<$type> may be either C<intra> or anything else. If it is not set to
835 C<intra>, then the search will exclude lost, very overdue, and
838 C<GetItemsInfo> returns a list of references-to-hash. Each element
839 contains a number of keys. Most of them are table items from the
840 C<biblio>, C<biblioitems>, C<items>, and C<itemtypes> tables in the
841 Koha database. Other keys include:
845 =item C<$data-E<gt>{branchname}>
847 The name (not the code) of the branch to which the book belongs.
849 =item C<$data-E<gt>{datelastseen}>
851 This is simply C<items.datelastseen>, except that while the date is
852 stored in YYYY-MM-DD format in the database, here it is converted to
853 DD/MM/YYYY format. A NULL date is returned as C<//>.
855 =item C<$data-E<gt>{datedue}>
857 =item C<$data-E<gt>{class}>
859 This is the concatenation of C<biblioitems.classification>, the book's
860 Dewey code, and C<biblioitems.subclass>.
862 =item C<$data-E<gt>{ocount}>
864 I think this is the number of copies of the book available.
866 =item C<$data-E<gt>{order}>
868 If this is set, it is set to C<One Order>.
875 my ( $biblionumber, $type ) = @_;
876 my $dbh = C4::Context->dbh;
877 my $query = "SELECT *,items.notforloan as itemnotforloan
879 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
880 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
881 $query .= (C4::Context->preference('item-level_itypes')) ?
882 " LEFT JOIN itemtypes on items.itype = itemtypes.itemtype "
883 : " LEFT JOIN itemtypes on biblioitems.itemtype = itemtypes.itemtype ";
884 $query .= "WHERE items.biblionumber = ? ORDER BY items.dateaccessioned desc" ;
885 my $sth = $dbh->prepare($query);
886 $sth->execute($biblionumber);
889 my ( $date_due, $count_reserves );
891 my $isth = $dbh->prepare(
892 "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
893 FROM issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
895 AND returndate IS NULL"
897 while ( my $data = $sth->fetchrow_hashref ) {
899 $isth->execute( $data->{'itemnumber'} );
900 if ( my $idata = $isth->fetchrow_hashref ) {
901 $data->{borrowernumber} = $idata->{borrowernumber};
902 $data->{cardnumber} = $idata->{cardnumber};
903 $data->{surname} = $idata->{surname};
904 $data->{firstname} = $idata->{firstname};
905 $datedue = $idata->{'date_due'};
906 if (C4::Context->preference("IndependantBranches")){
907 my $userenv = C4::Context->userenv;
908 if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
909 $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
913 if ( $datedue eq '' ) {
914 my ( $restype, $reserves ) =
915 C4::Reserves::CheckReserves( $data->{'itemnumber'} );
917 $count_reserves = $restype;
922 #get branch information.....
923 my $bsth = $dbh->prepare(
924 "SELECT * FROM branches WHERE branchcode = ?
927 $bsth->execute( $data->{'holdingbranch'} );
928 if ( my $bdata = $bsth->fetchrow_hashref ) {
929 $data->{'branchname'} = $bdata->{'branchname'};
931 $data->{'datedue'} = $datedue;
932 $data->{'count_reserves'} = $count_reserves;
934 # get notforloan complete status if applicable
935 my $sthnflstatus = $dbh->prepare(
936 'SELECT authorised_value
937 FROM marc_subfield_structure
938 WHERE kohafield="items.notforloan"
942 $sthnflstatus->execute;
943 my ($authorised_valuecode) = $sthnflstatus->fetchrow;
944 if ($authorised_valuecode) {
945 $sthnflstatus = $dbh->prepare(
946 "SELECT lib FROM authorised_values
948 AND authorised_value=?"
950 $sthnflstatus->execute( $authorised_valuecode,
951 $data->{itemnotforloan} );
952 my ($lib) = $sthnflstatus->fetchrow;
953 $data->{notforloan} = $lib;
956 # my stack procedures
957 my $stackstatus = $dbh->prepare(
958 'SELECT authorised_value
959 FROM marc_subfield_structure
960 WHERE kohafield="items.stack"
963 $stackstatus->execute;
965 ($authorised_valuecode) = $stackstatus->fetchrow;
966 if ($authorised_valuecode) {
967 $stackstatus = $dbh->prepare(
969 FROM authorised_values
971 AND authorised_value=?
974 $stackstatus->execute( $authorised_valuecode, $data->{stack} );
975 my ($lib) = $stackstatus->fetchrow;
976 $data->{stack} = $lib;
978 # Find the last 3 people who borrowed this item.
979 my $sth2 = $dbh->prepare("SELECT * FROM issues,borrowers
981 AND issues.borrowernumber = borrowers.borrowernumber
982 AND returndate IS NOT NULL LIMIT 3");
983 $sth2->execute($data->{'itemnumber'});
985 while (my $data2 = $sth2->fetchrow_hashref()) {
986 $data->{"timestamp$ii"} = $data2->{'timestamp'} if $data2->{'timestamp'};
987 $data->{"card$ii"} = $data2->{'cardnumber'} if $data2->{'cardnumber'};
988 $data->{"borrower$ii"} = $data2->{'borrowernumber'} if $data2->{'borrowernumber'};
992 $results[$i] = $data;
1000 =head2 get_itemnumbers_of
1004 my @itemnumbers_of = get_itemnumbers_of(@biblionumbers);
1008 Given a list of biblionumbers, return the list of corresponding itemnumbers
1009 for each biblionumber.
1011 Return a reference on a hash where keys are biblionumbers and values are
1012 references on array of itemnumbers.
1016 sub get_itemnumbers_of {
1017 my @biblionumbers = @_;
1019 my $dbh = C4::Context->dbh;
1025 WHERE biblionumber IN (?' . ( ',?' x scalar @biblionumbers - 1 ) . ')
1027 my $sth = $dbh->prepare($query);
1028 $sth->execute(@biblionumbers);
1032 while ( my ( $itemnumber, $biblionumber ) = $sth->fetchrow_array ) {
1033 push @{ $itemnumbers_of{$biblionumber} }, $itemnumber;
1036 return \%itemnumbers_of;
1039 =head1 LIMITED USE FUNCTIONS
1041 The following functions, while part of the public API,
1042 are not exported. This is generally because they are
1043 meant to be used by only one script for a specific
1044 purpose, and should not be used in any other context
1045 without careful thought.
1053 my $item_marc = GetMarcItem($biblionumber, $itemnumber);
1057 Returns MARC::Record of the item passed in parameter.
1058 This function is meant for use only in C<cataloguing/additem.pl>,
1059 where it is needed to support that script's MARC-like
1065 my ( $biblionumber, $itemnumber ) = @_;
1067 # GetMarcItem has been revised so that it does the following:
1068 # 1. Gets the item information from the items table.
1069 # 2. Converts it to a MARC field for storage in the bib record.
1071 # The previous behavior was:
1072 # 1. Get the bib record.
1073 # 2. Return the MARC tag corresponding to the item record.
1075 # The difference is that one treats the items row as authoritative,
1076 # while the other treats the MARC representation as authoritative
1077 # under certain circumstances.
1079 my $itemrecord = GetItem($itemnumber);
1081 # Tack on 'items.' prefix to column names so that TransformKohaToMarc will work.
1082 # Also, don't emit a subfield if the underlying field is blank.
1083 my $mungeditem = { map { $itemrecord->{$_} ne '' ? ("items.$_" => $itemrecord->{$_}) : () } keys %{ $itemrecord } };
1085 my $itemmarc = TransformKohaToMarc($mungeditem);
1090 =head1 PRIVATE FUNCTIONS AND VARIABLES
1092 The following functions are not meant to be called
1093 directly, but are documented in order to explain
1094 the inner workings of C<C4::Items>.
1098 =head2 %derived_columns
1100 This hash keeps track of item columns that
1101 are strictly derived from other columns in
1102 the item record and are not meant to be set
1105 Each key in the hash should be the name of a
1106 column (as named by TransformMarcToKoha). Each
1107 value should be hashref whose keys are the
1108 columns on which the derived column depends. The
1109 hashref should also contain a 'BUILDER' key
1110 that is a reference to a sub that calculates
1115 my %derived_columns = (
1116 'items.cn_sort' => {
1117 'itemcallnumber' => 1,
1118 'items.cn_source' => 1,
1119 'BUILDER' => \&_calc_items_cn_sort,
1123 =head2 _set_derived_columns_for_add
1127 _set_derived_column_for_add($item);
1131 Given an item hash representing a new item to be added,
1132 calculate any derived columns. Currently the only
1133 such column is C<items.cn_sort>.
1137 sub _set_derived_columns_for_add {
1140 foreach my $column (keys %derived_columns) {
1141 my $builder = $derived_columns{$column}->{'BUILDER'};
1142 my $source_values = {};
1143 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1144 next if $source_column eq 'BUILDER';
1145 $source_values->{$source_column} = $item->{$source_column};
1147 $builder->($item, $source_values);
1151 =head2 _set_derived_columns_for_mod
1155 _set_derived_column_for_mod($item);
1159 Given an item hash representing a new item to be modified.
1160 calculate any derived columns. Currently the only
1161 such column is C<items.cn_sort>.
1163 This routine differs from C<_set_derived_columns_for_add>
1164 in that it needs to handle partial item records. In other
1165 words, the caller of C<ModItem> may have supplied only one
1166 or two columns to be changed, so this function needs to
1167 determine whether any of the columns to be changed affect
1168 any of the derived columns. Also, if a derived column
1169 depends on more than one column, but the caller is not
1170 changing all of then, this routine retrieves the unchanged
1171 values from the database in order to ensure a correct
1176 sub _set_derived_columns_for_mod {
1179 foreach my $column (keys %derived_columns) {
1180 my $builder = $derived_columns{$column}->{'BUILDER'};
1181 my $source_values = {};
1182 my %missing_sources = ();
1183 my $must_recalc = 0;
1184 foreach my $source_column (keys %{ $derived_columns{$column} }) {
1185 next if $source_column eq 'BUILDER';
1186 if (exists $item->{$source_column}) {
1188 $source_values->{$source_column} = $item->{$source_column};
1190 $missing_sources{$source_column} = 1;
1194 foreach my $source_column (keys %missing_sources) {
1195 $source_values->{$source_column} = _get_single_item_column($source_column, $item->{'itemnumber'});
1197 $builder->($item, $source_values);
1202 =head2 _do_column_fixes_for_mod
1206 _do_column_fixes_for_mod($item);
1210 Given an item hashref containing one or more
1211 columns to modify, fix up certain values.
1212 Specifically, set to 0 any passed value
1213 of C<notforloan>, C<damaged>, C<itemlost>, or
1214 C<wthdrawn> that is either undefined or
1215 contains the empty string.
1219 sub _do_column_fixes_for_mod {
1222 if (exists $item->{'notforloan'} and
1223 (not defined $item->{'notforloan'} or $item->{'notforloan'} eq '')) {
1224 $item->{'notforloan'} = 0;
1226 if (exists $item->{'damaged'} and
1227 (not defined $item->{'damaged'} or $item->{'damaged'} eq '')) {
1228 $item->{'damaged'} = 0;
1230 if (exists $item->{'itemlost'} and
1231 (not defined $item->{'itemlost'} or $item->{'itemlost'} eq '')) {
1232 $item->{'itemlost'} = 0;
1234 if (exists $item->{'wthdrawn'} and
1235 (not defined $item->{'wthdrawn'} or $item->{'wthdrawn'} eq '')) {
1236 $item->{'wthdrawn'} = 0;
1240 =head2 _get_single_item_column
1244 _get_single_item_column($column, $itemnumber);
1248 Retrieves the value of a single column from an C<items>
1249 row specified by C<$itemnumber>.
1253 sub _get_single_item_column {
1255 my $itemnumber = shift;
1257 my $dbh = C4::Context->dbh;
1258 my $sth = $dbh->prepare("SELECT $column FROM items WHERE itemnumber = ?");
1259 $sth->execute($itemnumber);
1260 my ($value) = $sth->fetchrow();
1264 =head2 _calc_items_cn_sort
1268 _calc_items_cn_sort($item, $source_values);
1272 Helper routine to calculate C<items.cn_sort>.
1276 sub _calc_items_cn_sort {
1278 my $source_values = shift;
1280 $item->{'items.cn_sort'} = GetClassSort($source_values->{'items.cn_source'}, $source_values->{'itemcallnumber'}, "");
1283 =head2 _set_defaults_for_add
1287 _set_defaults_for_add($item_hash);
1291 Given an item hash representing an item to be added, set
1292 correct default values for columns whose default value
1293 is not handled by the DBMS. This includes the following
1300 C<items.dateaccessioned>
1322 sub _set_defaults_for_add {
1325 # if dateaccessioned is provided, use it. Otherwise, set to NOW()
1326 if (!(exists $item->{'dateaccessioned'}) ||
1327 ($item->{'dateaccessioned'} eq '')) {
1328 # FIXME add check for invalid date
1329 my $today = C4::Dates->new();
1330 $item->{'dateaccessioned'} = $today->output("iso"); #TODO: check time issues
1333 # various item status fields cannot be null
1334 $item->{'notforloan'} = 0 unless exists $item->{'notforloan'} and defined $item->{'notforloan'};
1335 $item->{'damaged'} = 0 unless exists $item->{'damaged'} and defined $item->{'damaged'};
1336 $item->{'itemlost'} = 0 unless exists $item->{'itemlost'} and defined $item->{'itemlost'};
1337 $item->{'wthdrawn'} = 0 unless exists $item->{'wthdrawn'} and defined $item->{'wthdrawn'};
1340 =head2 _koha_new_item
1344 my ($itemnumber,$error) = _koha_new_item( $dbh, $item, $barcode );
1348 Perform the actual insert into the C<items> table.
1352 sub _koha_new_item {
1353 my ( $dbh, $item, $barcode ) = @_;
1357 "INSERT INTO items SET
1359 biblioitemnumber = ?,
1361 dateaccessioned = ?,
1365 replacementprice = ?,
1366 replacementpricedate = NOW(),
1367 datelastborrowed = ?,
1368 datelastseen = NOW(),
1391 my $sth = $dbh->prepare($query);
1393 $item->{'biblionumber'},
1394 $item->{'biblioitemnumber'},
1396 $item->{'dateaccessioned'},
1397 $item->{'booksellerid'},
1398 $item->{'homebranch'},
1400 $item->{'replacementprice'},
1401 $item->{datelastborrowed},
1403 $item->{'notforloan'},
1405 $item->{'itemlost'},
1406 $item->{'wthdrawn'},
1407 $item->{'itemcallnumber'},
1408 $item->{'restricted'},
1409 $item->{'itemnotes'},
1410 $item->{'holdingbranch'},
1412 $item->{'location'},
1415 $item->{'renewals'},
1416 $item->{'reserves'},
1417 $item->{'items.cn_source'},
1418 $item->{'items.cn_sort'},
1421 $item->{'materials'},
1424 my $itemnumber = $dbh->{'mysql_insertid'};
1425 if ( defined $sth->errstr ) {
1426 $error.="ERROR in _koha_new_item $query".$sth->errstr;
1429 return ( $itemnumber, $error );
1432 =head2 _koha_modify_item
1436 my ($itemnumber,$error) =_koha_modify_item( $dbh, $item, $op );
1440 Perform the actual update of the C<items> row. Note that this
1441 routine accepts a hashref specifying the columns to update.
1445 sub _koha_modify_item {
1446 my ( $dbh, $item ) = @_;
1449 my $query = "UPDATE items SET ";
1451 for my $key ( keys %$item ) {
1453 push @bind, $item->{$key};
1456 $query .= " WHERE itemnumber=?";
1457 push @bind, $item->{'itemnumber'};
1458 my $sth = $dbh->prepare($query);
1459 $sth->execute(@bind);
1460 if ( $dbh->errstr ) {
1461 $error.="ERROR in _koha_modify_item $query".$dbh->errstr;
1465 return ($item->{'itemnumber'},$error);
1468 =head2 _koha_delete_item
1472 _koha_delete_item( $dbh, $itemnum );
1476 Internal function to delete an item record from the koha tables
1480 sub _koha_delete_item {
1481 my ( $dbh, $itemnum ) = @_;
1483 # save the deleted item to deleteditems table
1484 my $sth = $dbh->prepare("SELECT * FROM items WHERE itemnumber=?");
1485 $sth->execute($itemnum);
1486 my $data = $sth->fetchrow_hashref();
1488 my $query = "INSERT INTO deleteditems SET ";
1490 foreach my $key ( keys %$data ) {
1491 $query .= "$key = ?,";
1492 push( @bind, $data->{$key} );
1495 $sth = $dbh->prepare($query);
1496 $sth->execute(@bind);
1499 # delete from items table
1500 $sth = $dbh->prepare("DELETE FROM items WHERE itemnumber=?");
1501 $sth->execute($itemnum);
1506 =head2 _marc_from_item_hash
1510 my $item_marc = _marc_from_item_hash($item, $frameworkcode);
1514 Given an item hash representing a complete item record,
1515 create a C<MARC::Record> object containing an embedded
1516 tag representing that item.
1520 sub _marc_from_item_hash {
1522 my $frameworkcode = shift;
1524 # Tack on 'items.' prefix to column names so lookup from MARC frameworks will work
1525 # Also, don't emit a subfield if the underlying field is blank.
1526 my $mungeditem = { map { (defined($item->{$_}) and $item->{$_} ne '') ?
1527 (/^items\./ ? ($_ => $item->{$_}) : ("items.$_" => $item->{$_}))
1528 : () } keys %{ $item } };
1530 my $item_marc = MARC::Record->new();
1531 foreach my $item_field (keys %{ $mungeditem }) {
1532 my ($tag, $subfield) = GetMarcFromKohaField($item_field, $frameworkcode);
1533 next unless defined $tag and defined $subfield; # skip if not mapped to MARC field
1534 if (my $field = $item_marc->field($tag)) {
1535 $field->add_subfields($subfield => $mungeditem->{$item_field});
1537 $item_marc->add_fields( $tag, " ", " ", $subfield => $mungeditem->{$item_field});
1544 =head2 _add_item_field_to_biblio
1548 _add_item_field_to_biblio($item_marc, $biblionumber, $frameworkcode);
1552 Adds the fields from a MARC record containing the
1553 representation of a Koha item record to the MARC
1554 biblio record. The input C<$item_marc> record
1555 is expect to contain just one field, the embedded
1556 item information field.
1560 sub _add_item_field_to_biblio {
1561 my ($item_marc, $biblionumber, $frameworkcode) = @_;
1563 my $biblio_marc = GetMarcBiblio($biblionumber);
1565 foreach my $field ($item_marc->fields()) {
1566 $biblio_marc->append_fields($field);
1569 ModBiblioMarc($biblio_marc, $biblionumber, $frameworkcode);
1572 =head2 _replace_item_field_in_biblio
1576 &_replace_item_field_in_biblio($item_marc, $biblionumber, $itemnumber, $frameworkcode)
1580 Given a MARC::Record C<$item_marc> containing one tag with the MARC
1581 representation of the item, examine the biblio MARC
1582 for the corresponding tag for that item and
1583 replace it with the tag from C<$item_marc>.
1587 sub _replace_item_field_in_biblio {
1588 my ($ItemRecord, $biblionumber, $itemnumber, $frameworkcode) = @_;
1589 my $dbh = C4::Context->dbh;
1591 # get complete MARC record & replace the item field by the new one
1592 my $completeRecord = GetMarcBiblio($biblionumber);
1593 my ($itemtag,$itemsubfield) = GetMarcFromKohaField("items.itemnumber",$frameworkcode);
1594 my $itemField = $ItemRecord->field($itemtag);
1595 my @items = $completeRecord->field($itemtag);
1598 if ($_->subfield($itemsubfield) eq $itemnumber) {
1599 $_->replace_with($itemField);
1605 # If we haven't found the matching field,
1606 # just add it. However, this means that
1607 # there is likely a bug.
1608 $completeRecord->append_fields($itemField);
1612 ModBiblioMarc($completeRecord, $biblionumber, $frameworkcode);